gsdf
This commit is contained in:
@@ -158,14 +158,14 @@
|
||||
|
||||
|
||||
<div class="gapRight">
|
||||
@if(session)
|
||||
@if(!user_alternative_infobox)
|
||||
@if(!user_alternative_infobox && session)
|
||||
@if(user_has_favorited)
|
||||
<i class="iconset fa-solid fa-heart" id="a_favo" data-item-id="{{ item.id }}" title="Favorite"></i>
|
||||
@else
|
||||
<i class="iconset fa-regular fa-heart" id="a_favo" data-item-id="{{ item.id }}" title="Favorite"></i>
|
||||
@endif
|
||||
@endif
|
||||
@if(session)
|
||||
<i class="iconset fa-solid fa-circle-info" id="a_info" data-item-id="{{ item.id }}" title="{{ t('info_modal.button_title') || 'Post & File Info' }}"></i>
|
||||
<i class="iconset {{ isSubscribed ? 'fa-solid' : 'fa-regular' }} fa-bell" id="subscribe-btn" data-item-id="{{ item.id }}" title="{{ isSubscribed ? 'Subscribed' : 'Subscribe' }}"></i>
|
||||
<i class="iconset fa-solid fa-triangle-exclamation report-item-btn" data-item-id="{{ item.id }}" title="Report this post"></i>
|
||||
@@ -252,12 +252,10 @@
|
||||
@if(item.is_comments_locked) data-is-locked="true" @endif>
|
||||
@if(item.is_comments_locked && !is_mod_or_admin)
|
||||
<div class="lock-notice">🔒 Comments are disabled on this thread.</div>
|
||||
@elseif(session)
|
||||
@else
|
||||
<div class="comment-input main-input">
|
||||
<textarea disabled></textarea>
|
||||
</div>
|
||||
@else
|
||||
<div class="login-placeholder"><a href="/login" class="login-trigger-btn">Login</a> to comment</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -21,12 +21,10 @@
|
||||
@if(item.is_comments_locked) data-is-locked="true" @endif>
|
||||
@if(item.is_comments_locked && !is_mod_or_admin)
|
||||
<div class="lock-notice">🔒 Comments are disabled on this thread.</div>
|
||||
@elseif(session)
|
||||
@else
|
||||
<div class="comment-input main-input">
|
||||
<textarea disabled></textarea>
|
||||
</div>
|
||||
@else
|
||||
<div class="login-placeholder"><a href="/login" class="login-trigger-btn">Login</a> to comment</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@@ -158,6 +156,8 @@
|
||||
@else
|
||||
<i class="iconset fa-regular fa-heart" id="a_favo" data-item-id="{{ item.id }}" title="Favorite"></i>
|
||||
@endif
|
||||
@endif
|
||||
@if(session)
|
||||
<i class="iconset fa-solid fa-circle-info" id="a_info" data-item-id="{{ item.id }}" title="{{ t('info_modal.button_title') || 'Post & File Info' }}"></i>
|
||||
<i class="iconset {{ isSubscribed ? 'fa-solid' : 'fa-regular' }} fa-bell" id="subscribe-btn" data-item-id="{{ item.id }}" title="{{ isSubscribed ? 'Subscribed' : 'Subscribe' }}"></i>
|
||||
<i class="iconset fa-solid fa-triangle-exclamation report-item-btn" data-item-id="{{ item.id }}" title="Report this post"></i>
|
||||
|
||||
+67
-7
@@ -6,19 +6,26 @@
|
||||
|
||||
<!-- Quick navigation -->
|
||||
<nav id="settings-quicknav" aria-label="Settings sections">
|
||||
@if(!session.is_anon)
|
||||
<a href="#profile"><i class="fa-solid fa-user-pen"></i> {{ t('settings.profile') }}</a>
|
||||
@endif
|
||||
<a href="#preferences"><i class="fa-solid fa-sliders"></i> {{ t('settings.preferences') }}</a>
|
||||
@if(enable_data_export)
|
||||
@if(session.is_anon)
|
||||
<a href="#anon-ssh"><i class="fa-solid fa-key"></i> SSH Identity</a>
|
||||
@endif
|
||||
@if(!session.is_anon && enable_data_export)
|
||||
<a href="#export"><i class="fa-solid fa-file-export"></i> {{ t('settings.export_data_title') }}</a>
|
||||
@endif
|
||||
@if(!session.is_anon)
|
||||
<a href="#account"><i class="fa-solid fa-shield-halved"></i> {{ t('settings.account') }}</a>
|
||||
@if(matrix_enabled || telegram_enabled)
|
||||
@endif
|
||||
@if(!session.is_anon && (matrix_enabled || telegram_enabled))
|
||||
<a href="#linked"><i class="fa-solid fa-link"></i> {{ t('settings.linked_accounts') }}</a>
|
||||
@endif
|
||||
@if(enable_user_api_keys)
|
||||
@if(!session.is_anon && enable_user_api_keys)
|
||||
<a href="#apikey"><i class="fa-solid fa-key"></i> API Key</a>
|
||||
@endif
|
||||
@if(enable_user_invites)
|
||||
@if(!session.is_anon && enable_user_invites)
|
||||
<a href="#invites"><i class="fa-solid fa-ticket"></i> {{ t('invites.section_title') }}</a>
|
||||
@endif
|
||||
</nav>
|
||||
@@ -74,6 +81,7 @@
|
||||
</script>
|
||||
|
||||
|
||||
@if(!session.is_anon)
|
||||
<!-- ═══════════════════════════════ PROFILE ═══════════════════════════════ -->
|
||||
<h2 id="profile"><i class="fa-solid fa-user-pen"></i> {{ t('settings.profile') }}</h2>
|
||||
|
||||
@@ -187,6 +195,7 @@
|
||||
@endif
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
@@ -510,6 +519,55 @@
|
||||
|
||||
</div>
|
||||
|
||||
@if(session.is_anon)
|
||||
<!-- ═══════════════════════════════ ANONYMOUS SSH IDENTITY ═══════════════════════════════ -->
|
||||
<h2 id="anon-ssh"><i class="fa-solid fa-key"></i> Anonymous SSH Identity</h2>
|
||||
<div class="account-settings-wrapper" style="background: rgba(0,0,0,0.1); padding: 20px; border-radius: 6px; border: 1px solid var(--nav-border-color); margin-bottom: 30px;">
|
||||
<p style="color: var(--text-muted); margin-bottom: 16px;">
|
||||
You are currently browsing anonymously using an OpenSSH Ed25519 keypair. Your favorites and comments are tied to this cryptographic identity without requiring a username, password, or email.
|
||||
</p>
|
||||
|
||||
<div style="margin-bottom: 16px;">
|
||||
<label style="font-weight: bold; color: var(--text-muted); display: block; margin-bottom: 6px;">OpenSSH SHA256 Fingerprint</label>
|
||||
<code id="settings-anon-fp" style="background: rgba(0,0,0,0.3); padding: 6px 12px; border-radius: 4px; display: inline-block; color: #5bc0be; font-family: monospace;">{{ session.fingerprint || 'Loading...' }}</code>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label style="font-weight: bold; color: var(--text-muted); display: block; margin-bottom: 6px;">OpenSSH Public Key</label>
|
||||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||||
<input type="text" id="settings-anon-pubkey" readonly class="input" style="flex: 1; min-width: 250px; font-family: monospace; font-size: 0.85em;" value="" placeholder="Loading public key...">
|
||||
<button type="button" class="button" onclick="if(window.f0ckAnonSSH) window.f0ckAnonSSH.copyPublicKey();"><i class="fa-solid fa-copy"></i> Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 20px;">
|
||||
<button type="button" class="button button-primary" onclick="if(window.f0ckAnonSSH) window.f0ckAnonSSH.downloadPrivateKey();"><i class="fa-solid fa-download"></i> Download id_ed25519</button>
|
||||
<button type="button" class="button" onclick="if(window.f0ckAnonSSH) window.f0ckAnonSSH.downloadPublicKey();"><i class="fa-solid fa-download"></i> Download id_ed25519.pub</button>
|
||||
<button type="button" class="button" onclick="const m = document.getElementById('anon-ssh-modal'); if(m) m.style.display='flex';"><i class="fa-solid fa-key"></i> Import / Supply Key</button>
|
||||
</div>
|
||||
|
||||
<div style="padding: 12px 16px; background: rgba(255,255,255,0.03); border-radius: 6px; border: 1px solid rgba(255,255,255,0.08); font-size: 0.88em; color: var(--text-muted);">
|
||||
<i class="fa-solid fa-circle-info" style="color: var(--accent); margin-right: 6px;"></i>
|
||||
<strong>Want full features?</strong> Registered accounts can upload items, customize avatars, create API keys, and invite friends. You can <a href="#" onclick="event.preventDefault(); const m=document.getElementById('register-modal'); if(m) m.style.display='flex';" style="color: var(--accent); text-decoration: underline;">register an account</a> at any time.
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
const syncKey = () => {
|
||||
if (window.f0ckAnonSSH) {
|
||||
const pk = document.getElementById('settings-anon-pubkey');
|
||||
if (pk && window.f0ckAnonSSH.pubkey) pk.value = window.f0ckAnonSSH.pubkey;
|
||||
const fp = document.getElementById('settings-anon-fp');
|
||||
if (fp && window.f0ckAnonSSH.fingerprint) fp.textContent = window.f0ckAnonSSH.fingerprint;
|
||||
}
|
||||
};
|
||||
syncKey();
|
||||
window.addEventListener('f0ck:anon_session_ready', syncKey);
|
||||
document.addEventListener('DOMContentLoaded', syncKey);
|
||||
})();
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@if(enable_data_export)
|
||||
<!-- ═══════════════════════════════ EXPORT ═══════════════════════════════ -->
|
||||
<h2 id="export"><i class="fa-solid fa-file-export"></i> {{ t('settings.export_data_title') || 'Export Data' }}</h2>
|
||||
@@ -553,6 +611,7 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(!session.is_anon)
|
||||
<!-- ═══════════════════════════════ ACCOUNT ═══════════════════════════════ -->
|
||||
<h2 id="account"><i class="fa-solid fa-shield-halved"></i> {{ t('settings.account') }}</h2>
|
||||
<div class="account-settings-wrapper"
|
||||
@@ -612,8 +671,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(matrix_enabled || telegram_enabled)
|
||||
@if(!session.is_anon && (matrix_enabled || telegram_enabled))
|
||||
<!-- ═══════════════════════════════ LINKED ACCOUNTS ═══════════════════════════════ -->
|
||||
<h2 id="linked"><i class="fa-solid fa-link"></i> {{ t('settings.linked_accounts') }}</h2>
|
||||
<div class="linked-accounts-wrapper" style="background: rgba(0,0,0,0.1); padding: 20px; border-radius: 6px; border: 1px solid var(--nav-border-color); margin-bottom: 30px;">
|
||||
@@ -650,7 +710,7 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(enable_user_api_keys)
|
||||
@if(!session.is_anon && enable_user_api_keys)
|
||||
<!-- ═══════════════════════════════ API KEY ═══════════════════════════════ -->
|
||||
<h2 id="apikey"><i class="fa-solid fa-key"></i> Upload API Key</h2>
|
||||
<div id="api-key-section" class="account-settings-wrapper"
|
||||
@@ -686,7 +746,7 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(enable_user_invites)
|
||||
@if(!session.is_anon && enable_user_invites)
|
||||
<!-- ═══════════════════════════════ INVITES ═══════════════════════════════ -->
|
||||
<h2 id="invites"><i class="fa-solid fa-ticket"></i> {{ t('invites.section_title') }}</h2>
|
||||
<div id="invite-section" class="account-settings-wrapper"
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
@endif
|
||||
@if(private_society && !session)
|
||||
<script>
|
||||
window.f0ckSession = { logged_in: false, onara: @if(onara) true @else false @endif, enable_item_slugs: @if(enable_item_slugs) true @else false @endif, hide_comments_from_public: @if(hide_comments_from_public) true @else false @endif, guest_anonymize: @if(guest_anonymize) true @else false @endif, enable_xd_score: @if(enable_xd_score) true @else false @endif, default_theme: "{{ default_theme }}", show_content_warning: @if(show_content_warning) true @else false @endif, use_new_layout: @if(default_layout === 'legacy')false @else true @endif, comment_display_mode: {{ comment_display_mode }}, comment_max_length: {{ comment_max_length !== null && comment_max_length !== undefined ? comment_max_length : 'null' }}, development: @if(development) true @else false @endif, allow_comment_deletion: @if(allow_comment_deletion) true @else false @endif, hide_sidebar_default: @if(hide_sidebar_default) true @else false @endif, public_untagged: @if(public_untagged) true @else false @endif, public_nsfw: @if(public_nsfw) true @else false @endif };
|
||||
window.f0ckSession = { logged_in: false, onara: @if(onara) true @else false @endif, enable_item_slugs: @if(enable_item_slugs) true @else false @endif, enable_anonymous_access: @if(enable_anonymous_access) true @else false @endif, hide_comments_from_public: @if(hide_comments_from_public) true @else false @endif, guest_anonymize: @if(guest_anonymize) true @else false @endif, enable_xd_score: @if(enable_xd_score) true @else false @endif, default_theme: "{{ default_theme }}", show_content_warning: @if(show_content_warning) true @else false @endif, use_new_layout: @if(default_layout === 'legacy')false @else true @endif, comment_display_mode: {{ comment_display_mode }}, comment_max_length: {{ comment_max_length !== null && comment_max_length !== undefined ? comment_max_length : 'null' }}, development: @if(development) true @else false @endif, allow_comment_deletion: @if(allow_comment_deletion) true @else false @endif, hide_sidebar_default: @if(hide_sidebar_default) true @else false @endif, public_untagged: @if(public_untagged) true @else false @endif, public_nsfw: @if(public_nsfw) true @else false @endif };
|
||||
window.f0ckDebug = window.f0ckSession.development ? console.log.bind(console) : () => {};
|
||||
window.f0ckDefaultThumbSize = "{{ default_thumb_size }}";
|
||||
(() => {
|
||||
@@ -421,6 +421,7 @@
|
||||
public_untagged: @if(public_untagged) true @else false @endif,
|
||||
public_nsfw: @if(public_nsfw) true @else false @endif,
|
||||
enable_item_slugs: @if(enable_item_slugs) true @else false @endif,
|
||||
enable_anonymous_access: @if(enable_anonymous_access) true @else false @endif,
|
||||
strict_mode: @if(session && session.strict_mode) true @else false @endif,
|
||||
logged_in: @if(session) true @else false @endif,
|
||||
hide_comments_from_public: @if(hide_comments_from_public) true @else false @endif,
|
||||
@@ -436,6 +437,9 @@
|
||||
nsfl_tag_id: {{ nsfl_tag_id }},
|
||||
enable_nsfl: @if(enable_nsfl) true @else false @endif,
|
||||
user: @if(session) "{{ session.user }}" @else null @endif,
|
||||
login: @if(session) "{{ session.login }}" @else null @endif,
|
||||
anon_login: @if(session && session.anon_login) "{{ session.anon_login }}" @else null @endif,
|
||||
is_anon: @if(session && session.is_anon) true @else false @endif,
|
||||
display_name: @if(session && session.display_name) "{!! session.display_name !!}" @else null @endif,
|
||||
username_color: @if(session && session.username_color) "{!! session.username_color !!}" @else null @endif,
|
||||
id: @if(session && session.id) {{ session.id }} @else null @endif,
|
||||
@@ -661,9 +665,19 @@
|
||||
url_tracker_extracting: "{{ t('upload.url_tracker_extracting') || 'Extracting' }}",
|
||||
url_tracker_complete: "{{ t('upload.url_tracker_complete') || 'Complete!' }}",
|
||||
url_tracker_failed: "{{ t('upload.url_tracker_failed') || 'Upload failed' }}",
|
||||
url_tracker_view: "{{ t('upload.url_tracker_view') || 'View →' }}"
|
||||
url_tracker_view: "{{ t('upload.url_tracker_view') || 'View →' }}",
|
||||
// favorites
|
||||
fav_added: "{{ t('notifications.fav_added') || 'ADDED TO FAVORITES' }}",
|
||||
fav_removed: "{{ t('notifications.fav_removed') || 'REMOVED FROM FAVORITES' }}",
|
||||
no_favs: "{{ t('profile.no_favs') || 'no favorites' }}",
|
||||
favs_label: "{{ t('profile.favs_label') || 'Favorites' }}",
|
||||
guest_favs_saved: "{{ t('profile.guest_favs_saved') || 'You have {count} guest favorites saved on this device.' }}",
|
||||
sync_guest_favs: "{{ t('profile.sync_guest_favs') || 'Import to Account' }}",
|
||||
guest_favs_imported: "{{ t('profile.guest_favs_imported') || 'Imported favorites to your account!' }}",
|
||||
dismiss: "{{ t('common.dismiss') || 'Dismiss' }}"
|
||||
};
|
||||
</script>
|
||||
<script src="/s/js/anon_ssh.js?v={{ ts }}"></script>
|
||||
<script src="/s/js/f0ckm.js?v={{ ts }}"></script>
|
||||
<script src="/s/js/sidebar-activity.js?v={{ ts }}"></script>
|
||||
<script src="/s/js/flash_yank.js?v={{ ts }}"></script>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="{{ domain }}">
|
||||
<meta name="csrf-token" content="{{ csrf_token }}">
|
||||
<script>(function(){var h=document.documentElement;if(localStorage.getItem('hideItemRatings')==='true')h.classList.add('hide-item-ratings-active');if(localStorage.getItem('blurNsfw')==='true')h.classList.add('blur-nsfw-active');if(localStorage.getItem('blurNsfl')==='true')h.classList.add('blur-nsfl-active');if(localStorage.getItem('blurSfw')==='true')h.classList.add('blur-sfw-active');if(localStorage.getItem('blurUntagged')==='true')h.classList.add('blur-untagged-active');if(localStorage.getItem('blurDetail')!=='false')h.classList.add('blur-detail-active');if(localStorage.getItem('imageExpandOnClick')!=='false')h.classList.add('image-expand-active');var ts=localStorage.getItem('scroller_thumb_size');if(ts&&['s','m','l','xl'].includes(ts))h.dataset.thumbSize=ts;})();</script>
|
||||
<style>
|
||||
html { background-color: #000; color: #fff; }
|
||||
|
||||
+212
-7
@@ -14,7 +14,9 @@
|
||||
<!-- Nav links: desktop always-on, mobile hidden until toggled -->
|
||||
<div class="nav-collapse" id="navbarContent">
|
||||
<div class="nav-links">
|
||||
@if(!session.is_anon)
|
||||
<a id="nav-upload-link" style="cursor:pointer;"><i class="fa-solid fa-angle-up"></i> {{ t('nav.upload') }}</a>
|
||||
@endif
|
||||
@if(meme_creator)
|
||||
<a id="nav-meme-link" href="/meme"><i class="fa-regular fa-image"></i> {{ t('nav.meme') }}</a>
|
||||
@endif
|
||||
@@ -42,7 +44,9 @@
|
||||
<!-- Avatar dropdown -->
|
||||
<div class="nav-user-dropdown">
|
||||
<button class="nav-user-btn nav-avatar-btn" id="nav-user-toggle">
|
||||
@if(session.avatar_file)
|
||||
@if(session.is_anon)
|
||||
<span class="nav-avatar-icon" style="font-size: 1.2em; margin-right: 6px; display: inline-flex; align-items: center; cursor: pointer;" onclick="event.stopPropagation(); const u = '{{ session.login.toLowerCase() }}'; if(typeof window.loadPageAjax==='function'){ window.loadPageAjax('/user/' + u, true); } else { window.location.href='/user/' + u; }" title="{{ session.login }}"><i class="fa-solid fa-user-secret"></i></span>
|
||||
@elseif(session.avatar_file)
|
||||
<img class="nav-avatar-img" src="/a/{{ session.avatar_file }}" onerror="this.src='/a/default.png'" onclick="event.stopPropagation(); if(typeof window.loadPageAjax==='function'){ window.loadPageAjax('/user/{!! session.user.toLowerCase() !!}', true); } else { window.location.href='/user/{!! session.user.toLowerCase() !!}'; }" title="{{ session.user }}">
|
||||
@elseif(session.avatar && session.avatar > 0)
|
||||
<img class="nav-avatar-img" src="/t/{{ session.avatar }}.webp" onerror="this.src='/a/default.png'" onclick="event.stopPropagation(); if(typeof window.loadPageAjax==='function'){ window.loadPageAjax('/user/{!! session.user.toLowerCase() !!}', true); } else { window.location.href='/user/{!! session.user.toLowerCase() !!}'; }" title="{{ session.user }}">
|
||||
@@ -53,11 +57,18 @@
|
||||
<span class="nav-avatar-caret">▾</span>
|
||||
</button>
|
||||
<div class="nav-user-menu" id="nav-user-menu">
|
||||
@if(session.is_anon)
|
||||
<a href="/user/{{ session.login.toLowerCase() }}">{{ t('nav.profile') }}</a>
|
||||
@else
|
||||
<a href="/user/{!! session.user.toLowerCase() !!}">{{ t('nav.profile') }}</a>
|
||||
@if(userhalls_enabled)
|
||||
<a href="/user/{!! session.user.toLowerCase() !!}/halls">{{ t('nav.my_halls') }}</a>
|
||||
@endif
|
||||
<a href="/user/{{ session.user.toLowerCase() }}/favs" class="mobile-only">{{ t('nav.favs') }}</a>
|
||||
@endif
|
||||
@if(enable_anonymous_access && session.is_anon)
|
||||
<a href="#" id="nav-user-anon-identity-btn" onclick="event.preventDefault(); if(window.f0ckAnonSSH) { window.f0ckAnonSSH.openModal(); } else { const m = document.getElementById('anon-ssh-modal'); if (m) m.style.display='flex'; }"><i class="fa-solid fa-key"></i> Key Management</a>
|
||||
@endif
|
||||
<a href="/user/{{ (session.is_anon && session.login ? session.login : session.user).toLowerCase() }}/favs" class="mobile-only">{{ t('nav.favs') }}</a>
|
||||
<a href="/settings" class="mobile-only">{{ t('nav.settings') }}</a>
|
||||
<div class="nav-user-divider"></div>
|
||||
<a href="/logout" class="mobile-only">{{ t('nav.logout') }}</a>
|
||||
@@ -91,10 +102,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Quick Favs -->
|
||||
<a href="/user/{{ session.user.toLowerCase() }}/favs" title="Favorites" class="desktop-only"><i class="fa-solid fa-heart"></i></a>
|
||||
<a href="/user/{{ (session.is_anon && session.login ? session.login : session.user).toLowerCase() }}/favs" title="Favorites" class="desktop-only"><i class="fa-solid fa-heart"></i></a>
|
||||
|
||||
<!-- DM -->
|
||||
@if(private_messages)
|
||||
@if(private_messages && !session.is_anon)
|
||||
<div id="nav-dm" class="nav-item-rel">
|
||||
<a href="/messages" id="nav-dm-btn" title="Direct Messages">
|
||||
<i class="fa-solid fa-envelope"></i>
|
||||
@@ -261,13 +272,32 @@
|
||||
|
||||
<div class="nav-right-group">
|
||||
<div class="nav-user-dropdown">
|
||||
<button class="nav-user-btn" id="nav-visitor-toggle"><i class="fa-solid fa-user-secret"></i> {{ t('nav.guest') }} <span class="nav-avatar-caret">▾</span></button>
|
||||
<div class="nav-user-menu" id="nav-visitor-menu">
|
||||
<button class="nav-user-btn" id="nav-visitor-toggle">
|
||||
<i id="nav-visitor-icon" class="fa-solid @if(enable_anonymous_access && session && session.is_anon) fa-user-secret @else fa-user @endif"></i>
|
||||
<span id="nav-anon-label">@if(enable_anonymous_access && session && session.is_anon)anonymous@else guest @endif</span>
|
||||
<span class="nav-avatar-caret">▾</span>
|
||||
</button>
|
||||
<div class="nav-user-menu" id="nav-visitor-menu" style="min-width: max-content; width: max-content; white-space: nowrap;">
|
||||
@if(enable_anonymous_access && session && session.is_anon)
|
||||
<a href="/favs" id="nav-guest-favs-link" class="mobile-only">{{ t('nav.favs') }}</a>
|
||||
@endif
|
||||
@if(enable_anonymous_access)
|
||||
<a href="#" id="nav-login-anon-btn" @if(session && session.is_anon) style="display:none;" @endif><i class="fa-solid fa-user-secret"></i> Login as anonymous</a>
|
||||
<a href="#" id="nav-anon-identity-btn" @if(!session || !session.is_anon) style="display:none;" @endif onclick="event.preventDefault(); if(window.f0ckAnonSSH) { window.f0ckAnonSSH.openModal(); } else { const m = document.getElementById('anon-ssh-modal'); if (m) m.style.display='flex'; }"><i class="fa-solid fa-key"></i> Key Management</a>
|
||||
<a href="/settings" id="nav-anon-settings-btn" @if(!session || !session.is_anon) style="display:none;" @endif><i class="fa-solid fa-gear"></i> Settings</a>
|
||||
@endif
|
||||
<a href="#" id="nav-login-btn">{{ t('nav.login') }}</a>
|
||||
<a href="#" id="nav-register-btn">{{ t('nav.register') }}</a>
|
||||
<div class="nav-user-divider"></div>
|
||||
@if(enable_anonymous_access)
|
||||
<div class="nav-user-divider" id="nav-anon-divider" @if(!session || !session.is_anon) style="display:none;" @endif></div>
|
||||
<a href="#" id="nav-anon-logout-btn" @if(!session || !session.is_anon) style="display:none;" @endif><i class="fa-solid fa-right-from-bracket"></i> Logout (Guest)</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@if(enable_anonymous_access && session && session.is_anon)
|
||||
<!-- Quick Favs for Anon -->
|
||||
<a href="/favs" id="nav-guest-favs" title="{{ t('nav.favs') }}" class="desktop-only"><i class="fa-solid fa-heart"></i></a>
|
||||
@endif
|
||||
<button class="navbar-toggler" type="button" id="nav-toggler"
|
||||
onclick="document.getElementById('navbarContent').classList.toggle('show'); this.classList.toggle('is-open')">
|
||||
<span></span><span></span><span></span>
|
||||
@@ -309,6 +339,13 @@
|
||||
{{ t('auth.no_account') }} <a href="#" id="login-to-register" style="color: var(--accent); text-decoration: underline;">{{ t('auth.register_now') }}</a>
|
||||
</p>
|
||||
@endif
|
||||
@if(enable_anonymous_access)
|
||||
<div style="margin-top: 15px; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 12px; text-align: center;">
|
||||
<button type="button" id="modal-login-as-anon-btn" class="btn btn-sm" style="background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.2); color: #ddd; border-radius: 4px; padding: 7px 14px; font-size: 0.88em; cursor: pointer; width: 100%; transition: background 0.2s;">
|
||||
<i class="fa-solid fa-user-secret"></i> Login as anonymous
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -346,6 +383,174 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(enable_anonymous_access)
|
||||
<!-- Anonymous OpenSSH Ed25519 Identity Modal -->
|
||||
<div id="anon-ssh-modal" style="display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.75); backdrop-filter: blur(4px); z-index: 99999; align-items: center; justify-content: center;">
|
||||
<div class="login-modal-content" style="max-width: 540px; width: 92vw; max-height: 90vh; overflow-y: auto; text-align: left; padding: 25px; border: 1px solid rgba(255,255,255,0.15); border-radius: 8px; background: var(--bg-primary, #111); box-shadow: 0 10px 40px rgba(0,0,0,0.8);">
|
||||
<button id="anon-ssh-modal-close" style="position: absolute; top: 15px; right: 15px; background: none; border: none; color: var(--text-muted, #aaa); font-size: 1.4em; cursor: pointer; line-height: 1;">×</button>
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
|
||||
<i class="fa-solid fa-key" style="color: var(--accent, #0096ff); font-size: 1.3em;"></i>
|
||||
<h3 style="margin: 0; font-size: 1.25em; color: var(--text-color, #fff);">Anonymous SSH Identity</h3>
|
||||
</div>
|
||||
<p style="margin: 0 0 15px 0; font-size: 0.85em; color: var(--text-muted, #aaa); line-height: 1.4;">
|
||||
Your browser holds an <strong>OpenSSH Ed25519</strong> private key. Your comments and favorites belong to this key without needing a password.
|
||||
</p>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 15px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 8px;">
|
||||
<button type="button" id="anon-tab-btn-identity" class="btn btn-sm btn-primary" style="font-size: 0.85em; padding: 5px 12px;">My Identity</button>
|
||||
<button type="button" id="anon-tab-btn-import" class="btn btn-sm btn-secondary" style="font-size: 0.85em; padding: 5px 12px;">Import / Supply Key</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab 1: Current Identity -->
|
||||
<div id="anon-tab-identity">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-muted, #888); margin-bottom: 4px;">Fingerprint</label>
|
||||
<div style="display: flex; align-items: center; gap: 8px; background: rgba(0,0,0,0.4); padding: 8px 12px; border-radius: 4px; border: 1px solid rgba(255,255,255,0.08);">
|
||||
<code id="anon-ssh-fp-display" style="font-family: monospace; font-size: 0.88em; color: var(--accent, #00d2ff); word-break: break-all; flex: 1;">Generating...</code>
|
||||
<button type="button" id="anon-copy-fp-btn" title="Copy Fingerprint" class="btn btn-sm" style="background: transparent; border: none; color: #aaa; cursor: pointer; padding: 4px;"><i class="fa-solid fa-copy"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-muted, #888); margin-bottom: 4px;">OpenSSH Public Key (<code>id_ed25519.pub</code>)</label>
|
||||
<textarea id="anon-ssh-pub-display" readonly rows="2" style="width: 100%; box-sizing: border-box; font-family: monospace; font-size: 0.82em; background: rgba(0,0,0,0.4); color: #ddd; border: 1px solid rgba(255,255,255,0.08); border-radius: 4px; padding: 8px; resize: none;"></textarea>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<button type="button" id="anon-copy-pub-btn" class="btn btn-sm btn-secondary" style="font-size: 0.85em; padding: 6px 12px;"><i class="fa-solid fa-copy"></i> Copy Public Key</button>
|
||||
<button type="button" id="anon-dl-priv-btn" class="btn btn-sm btn-primary" style="font-size: 0.85em; padding: 6px 12px;"><i class="fa-solid fa-download"></i> Download id_ed25519</button>
|
||||
<button type="button" id="anon-dl-pub-btn" class="btn btn-sm btn-secondary" style="font-size: 0.85em; padding: 6px 12px;"><i class="fa-solid fa-download"></i> Download id_ed25519.pub</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab 2: Import Key -->
|
||||
<div id="anon-tab-import" style="display: none;">
|
||||
<p style="font-size: 0.85em; color: var(--text-muted, #aaa); margin-top: 0; margin-bottom: 10px;">
|
||||
Paste your existing <code>id_ed25519</code> OpenSSH private key or raw 32-byte seed to restore your anonymous identity on this browser.
|
||||
</p>
|
||||
<textarea id="anon-import-key-input" rows="4" placeholder="-----BEGIN OPENSSH PRIVATE KEY----- ... -----END OPENSSH PRIVATE KEY-----" style="width: 100%; box-sizing: border-box; font-family: monospace; font-size: 0.82em; background: rgba(0,0,0,0.4); color: #ddd; border: 1px solid rgba(255,255,255,0.08); border-radius: 4px; padding: 8px; resize: vertical; margin-bottom: 10px;"></textarea>
|
||||
|
||||
<input type="file" id="anon-import-file-elem" style="display: none;" />
|
||||
|
||||
<div style="display: flex; gap: 8px; align-items: center;">
|
||||
<button type="button" id="anon-upload-key-btn" class="btn btn-sm btn-secondary" style="font-size: 0.85em; padding: 6px 12px;"><i class="fa-solid fa-upload"></i> Upload File</button>
|
||||
<button type="button" id="anon-submit-import-btn" class="btn btn-sm btn-primary" style="font-size: 0.85em; padding: 6px 16px;"><i class="fa-solid fa-check"></i> Activate Key</button>
|
||||
</div>
|
||||
<div id="anon-import-status" style="margin-top: 10px; font-size: 0.85em; display: none;"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
// Tab switching
|
||||
const tabBtnId = document.getElementById('anon-tab-btn-identity');
|
||||
const tabBtnImp = document.getElementById('anon-tab-btn-import');
|
||||
const tabId = document.getElementById('anon-tab-identity');
|
||||
const tabImp = document.getElementById('anon-tab-import');
|
||||
if(tabBtnId && tabBtnImp && tabId && tabImp){
|
||||
tabBtnId.addEventListener('click', function(){
|
||||
tabId.style.display = 'block';
|
||||
tabImp.style.display = 'none';
|
||||
tabBtnId.classList.remove('btn-secondary'); tabBtnId.classList.add('btn-primary');
|
||||
tabBtnImp.classList.remove('btn-primary'); tabBtnImp.classList.add('btn-secondary');
|
||||
});
|
||||
tabBtnImp.addEventListener('click', function(){
|
||||
tabId.style.display = 'none';
|
||||
tabImp.style.display = 'block';
|
||||
tabBtnImp.classList.remove('btn-secondary'); tabBtnImp.classList.add('btn-primary');
|
||||
tabBtnId.classList.remove('btn-primary'); tabBtnId.classList.add('btn-secondary');
|
||||
});
|
||||
}
|
||||
|
||||
// Copy buttons
|
||||
const copyPubBtn = document.getElementById('anon-copy-pub-btn');
|
||||
if(copyPubBtn){
|
||||
copyPubBtn.addEventListener('click', function(){
|
||||
if(window.f0ckAnonSSH) window.f0ckAnonSSH.copyPublicKey();
|
||||
});
|
||||
}
|
||||
const copyFpBtn = document.getElementById('anon-copy-fp-btn');
|
||||
if(copyFpBtn){
|
||||
copyFpBtn.addEventListener('click', function(){
|
||||
const fp = document.getElementById('anon-ssh-fp-display')?.textContent;
|
||||
if(fp && navigator.clipboard){
|
||||
navigator.clipboard.writeText(fp).then(function(){
|
||||
if(typeof window.showToastNotification === 'function') window.showToastNotification('Fingerprint copied!');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Download buttons
|
||||
const dlPriv = document.getElementById('anon-dl-priv-btn');
|
||||
if(dlPriv){
|
||||
dlPriv.addEventListener('click', function(){
|
||||
if(window.f0ckAnonSSH) window.f0ckAnonSSH.downloadPrivateKey();
|
||||
});
|
||||
}
|
||||
const dlPub = document.getElementById('anon-dl-pub-btn');
|
||||
if(dlPub){
|
||||
dlPub.addEventListener('click', function(){
|
||||
if(window.f0ckAnonSSH) window.f0ckAnonSSH.downloadPublicKey();
|
||||
});
|
||||
}
|
||||
|
||||
// File upload trigger
|
||||
const uploadBtn = document.getElementById('anon-upload-key-btn');
|
||||
const fileElem = document.getElementById('anon-import-file-elem');
|
||||
const inputElem = document.getElementById('anon-import-key-input');
|
||||
if(uploadBtn && fileElem){
|
||||
uploadBtn.addEventListener('click', function(){ fileElem.click(); });
|
||||
fileElem.addEventListener('change', function(){
|
||||
if(fileElem.files && fileElem.files[0]){
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e){
|
||||
if(inputElem) inputElem.value = e.target.result;
|
||||
};
|
||||
reader.readAsText(fileElem.files[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Submit import
|
||||
const submitImpBtn = document.getElementById('anon-submit-import-btn');
|
||||
const statusEl = document.getElementById('anon-import-status');
|
||||
if(submitImpBtn){
|
||||
submitImpBtn.addEventListener('click', async function(){
|
||||
const keyVal = inputElem?.value;
|
||||
if(!keyVal || !keyVal.trim()){
|
||||
if(statusEl){ statusEl.style.display = 'block'; statusEl.style.color = '#ff4444'; statusEl.textContent = 'Please paste a key or select a file'; }
|
||||
return;
|
||||
}
|
||||
try {
|
||||
submitImpBtn.disabled = true;
|
||||
submitImpBtn.textContent = 'Importing...';
|
||||
await window.f0ckAnonSSH.importKey(keyVal);
|
||||
if(statusEl){
|
||||
statusEl.style.display = 'block';
|
||||
statusEl.style.color = '#00C851';
|
||||
statusEl.textContent = 'Key activated successfully! Reloading...';
|
||||
}
|
||||
setTimeout(function(){ window.location.reload(); }, 800);
|
||||
} catch(err){
|
||||
submitImpBtn.disabled = false;
|
||||
submitImpBtn.innerHTML = '<i class="fa-solid fa-check"></i> Activate Key';
|
||||
if(statusEl){
|
||||
statusEl.style.display = 'block';
|
||||
statusEl.style.color = '#ff4444';
|
||||
statusEl.textContent = 'Import error: ' + (err.message || err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@if(!private_society || session)
|
||||
<!-- Shortcuts Modal -->
|
||||
<div id="shortcuts-modal" style="display: none;">
|
||||
|
||||
Reference in New Issue
Block a user