This commit is contained in:
2026-08-19 20:38:53 +02:00
parent d700ed1aa5
commit 3e4f8c80b2
5 changed files with 676 additions and 344 deletions

View File

@@ -16117,72 +16117,428 @@ body.scroller-active #gchat-reopen-bubble {
}
/* =============================================
POST & FILE INFO MODAL STYLES
POST & FILE INFO MODAL (RETHOUGHT DESIGN)
============================================= */
.info-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
/* Modal Container & Animation */
.infobox-modal-content {
max-width: 580px !important;
width: 92% !important;
padding: 0 !important;
background: rgba(22, 23, 27, 0.94) !important;
backdrop-filter: blur(16px) saturate(180%) !important;
-webkit-backdrop-filter: blur(16px) saturate(180%) !important;
border: 1px solid rgba(255, 255, 255, 0.12) !important;
border-radius: 14px !important;
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.7), 0 0 40px rgba(var(--accent-rgb, 153, 255, 0), 0.08) !important;
overflow: hidden !important;
display: flex !important;
flex-direction: column !important;
text-align: left !important;
animation: infobox-pop-in 0.22s cubic-bezier(0.16, 1, 0.3, 1) !important;
}
.info-table th {
text-align: left;
padding: 8px 10px 8px 0;
font-weight: bold;
@keyframes infobox-pop-in {
from {
opacity: 0;
transform: scale(0.95) translateY(10px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
/* Modal Header */
.infobox-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
background: rgba(255, 255, 255, 0.03);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.infobox-header-title {
display: flex;
align-items: center;
gap: 12px;
}
.infobox-icon-badge {
width: 38px;
height: 38px;
border-radius: 10px;
background: color-mix(in srgb, var(--accent, #9f0) 18%, transparent);
border: 1px solid color-mix(in srgb, var(--accent, #9f0) 40%, transparent);
color: var(--accent, #9f0);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.1rem;
flex-shrink: 0;
box-shadow: 0 0 12px color-mix(in srgb, var(--accent, #9f0) 25%, transparent);
}
.infobox-header-text h3 {
margin: 0;
font-size: 1.1rem;
font-weight: 700;
color: var(--white, #fff);
letter-spacing: -0.01em;
}
.infobox-sub-heading {
font-size: 0.8rem;
color: var(--text-muted, #999);
display: block;
margin-top: 2px;
}
.infobox-sub-heading code {
font-family: var(--font-mono, monospace);
background: rgba(255, 255, 255, 0.08);
padding: 1px 5px;
border-radius: 4px;
color: var(--accent, #9f0);
font-size: 0.85em;
}
.infobox-close-icon-btn {
background: transparent;
border: none;
color: #888;
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 1.1rem;
transition: all 0.15s ease;
}
.infobox-close-icon-btn:hover {
background: rgba(255, 255, 255, 0.12);
color: #fff;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
font-size: 0.9em;
width: 35%;
}
.info-table td {
text-align: left;
padding: 8px 0;
color: #ccc;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
font-size: 0.9em;
/* Modal Body */
.infobox-modal-body {
padding: 18px 20px;
max-height: 72vh;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 20px;
}
/* Custom Scrollbar for Modal Body */
.infobox-modal-body::-webkit-scrollbar {
width: 6px;
}
.infobox-modal-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.15);
}
.infobox-modal-body::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
.infobox-modal-body::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.35);
}
/* Sections */
.infobox-section {
display: flex;
flex-direction: column;
gap: 10px;
}
.infobox-section-header {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.78rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--accent, #9f0);
opacity: 0.9;
}
/* Cards Grid */
.infobox-cards-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.infobox-card {
background: rgba(255, 255, 255, 0.035);
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 10px;
padding: 12px 14px;
display: flex;
flex-direction: column;
gap: 6px;
transition: border-color 0.18s ease, background 0.18s ease;
}
.infobox-card:hover {
background: rgba(255, 255, 255, 0.05);
border-color: rgba(255, 255, 255, 0.12);
}
.infobox-card.full-width {
grid-column: 1 / -1;
}
.infobox-card-label {
font-size: 0.76rem;
font-weight: 600;
color: #888;
display: flex;
align-items: center;
gap: 6px;
}
.infobox-card-label i {
font-size: 0.85em;
color: var(--accent, #9f0);
opacity: 0.8;
}
.infobox-card-label-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.infobox-card-value {
font-size: 0.92rem;
color: #eee;
word-break: break-word;
overflow-wrap: anywhere;
}
.info-table td a {
.infobox-card-value-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
flex-wrap: wrap;
}
.font-mono {
font-family: var(--font-mono, monospace);
}
/* Title Input */
.info-title-edit-wrap {
display: flex;
align-items: center;
gap: 6px;
margin-top: 2px;
}
.info-title-input {
flex: 1;
background: rgba(0, 0, 0, 0.35);
border: 1px solid rgba(255, 255, 255, 0.14);
border-radius: 6px;
color: #fff;
padding: 6px 10px;
font-size: 0.88rem;
outline: none;
transition: all 0.18s ease;
}
.info-title-input:focus {
border-color: var(--accent, #9f0);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent, #9f0) 25%, transparent);
background: rgba(0, 0, 0, 0.5);
}
.info-title-save-btn {
background: var(--accent, #9f0);
color: #111;
border: none;
border-radius: 6px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 0.9rem;
transition: transform 0.12s ease, opacity 0.15s ease;
flex-shrink: 0;
}
.info-title-save-btn:hover {
transform: scale(1.04);
opacity: 0.9;
}
.info-title-status {
font-size: 0.78rem;
margin-top: 2px;
display: block;
}
/* Visibility Badge & Action Buttons */
.visibility-badge {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 0.88rem;
font-weight: 600;
color: #fff;
}
.infobox-action-btn {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.14);
color: #ddd;
border-radius: 6px;
padding: 3px 10px;
font-size: 0.78rem;
font-weight: 600;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 5px;
transition: all 0.15s ease;
}
.infobox-action-btn:hover {
background: rgba(255, 255, 255, 0.16);
border-color: rgba(255, 255, 255, 0.25);
color: #fff;
}
/* Expiration Controls */
.infobox-expiry-content {
display: flex;
flex-direction: column;
gap: 6px;
}
.expiry-status-label {
font-size: 0.88rem;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 6px;
}
.info-expiry-edit-controls {
display: flex;
align-items: center;
gap: 6px;
}
.infobox-select {
background: rgba(0, 0, 0, 0.4);
color: #fff;
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 6px;
padding: 4px 8px;
font-size: 0.8rem;
outline: none;
}
.infobox-select:focus {
border-color: var(--accent, #9f0);
}
/* MIME Pill & Link Pill */
.mime-pill {
background: rgba(255, 255, 255, 0.07);
border: 1px solid rgba(255, 255, 255, 0.12);
padding: 3px 8px;
border-radius: 5px;
font-family: var(--font-mono, monospace);
font-size: 0.82rem;
color: #e0e0e0;
}
.infobox-link-pill {
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--accent, #9f0);
background: color-mix(in srgb, var(--accent, #9f0) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--accent, #9f0) 30%, transparent);
padding: 4px 10px;
border-radius: 6px;
font-size: 0.82rem;
font-weight: 600;
text-decoration: none;
transition: all 0.15s ease;
}
.infobox-link-pill:hover {
background: color-mix(in srgb, var(--accent, #9f0) 22%, transparent);
border-color: var(--accent, #9f0);
color: var(--accent, #9f0);
text-decoration: none;
font-weight: 600;
word-break: break-all;
overflow-wrap: anywhere;
}
#info-modal .modal-content .info-table td a {
.infobox-source-link {
color: var(--accent, #9f0);
text-decoration: none;
font-weight: 500;
display: inline-flex;
align-items: center;
gap: 6px;
word-break: break-all;
overflow-wrap: anywhere;
font-size: 0.88rem;
}
.info-table td a:hover {
.infobox-source-link:hover {
text-decoration: underline;
}
.info-table td code {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 2px 6px;
/* Hash Codeblock & Copy Button */
.copy-hash-btn {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.14);
color: #bbb;
border-radius: 4px;
font-family: monospace;
font-size: 0.85em;
color: #eee;
padding: 2px 8px;
font-size: 0.72rem;
font-weight: 600;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 4px;
transition: all 0.15s ease;
}
.copy-hash-btn:hover {
background: var(--accent, #9f0);
border-color: var(--accent, #9f0);
color: #111;
}
.info-hash-codeblock {
margin: 2px 0;
padding: 6px 10px;
background: rgba(0, 0, 0, 0.4);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 4px;
margin: 2px 0 0 0;
padding: 8px 10px;
background: rgba(0, 0, 0, 0.45);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 6px;
white-space: pre-wrap;
word-break: break-all;
font-family: monospace;
font-size: 0.85em;
color: var(--text-color, #eee);
font-family: var(--font-mono, monospace);
font-size: 0.8rem;
color: #ddd;
user-select: all;
}
@@ -16192,66 +16548,97 @@ body.scroller-active #gchat-reopen-bubble {
padding: 0 !important;
}
.info-table tr:last-child th,
.info-table tr:last-child td {
border-bottom: none;
/* Repost Badges */
.infobox-reposts-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
/* ─── Info Modal: Inline Title Editor ─────────────────────────────────────── */
.repost-pill {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 10px;
border-radius: 6px;
font-size: 0.82rem;
font-weight: 600;
text-decoration: none;
transition: all 0.15s ease;
}
.info-title-edit-wrap {
.repost-pill.phash {
background: rgba(255, 170, 0, 0.12);
border: 1px solid rgba(255, 170, 0, 0.3);
color: #ffb732;
}
.repost-pill.exact {
background: rgba(0, 200, 81, 0.12);
border: 1px solid rgba(0, 200, 81, 0.3);
color: #00e676;
}
.repost-pill:hover {
transform: translateY(-1px);
filter: brightness(1.2);
text-decoration: none;
}
/* Modal Footer */
.infobox-modal-footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
padding: 14px 20px;
background: rgba(0, 0, 0, 0.25);
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.btn-infobox-action {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.15);
color: #eee;
padding: 8px 16px;
border-radius: 8px;
font-size: 0.84rem;
font-weight: 600;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.18s ease;
}
.info-title-input {
flex: 1;
background: rgba(255, 255, 255, 0.07);
border: 1px solid rgba(255, 255, 255, 0.15);
color: var(--white, #eee);
padding: 5px 8px;
font-size: 0.88em;
font-family: inherit;
outline: none;
border-radius: 0;
transition: border-color 0.15s;
min-width: 0;
.btn-infobox-action:hover {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 255, 255, 0.28);
color: #fff;
}
.info-title-input:focus {
border-color: var(--accent, #9f0);
background: rgba(255, 255, 255, 0.1);
}
.info-title-save-btn {
flex-shrink: 0;
.btn-infobox-close {
background: var(--accent, #9f0);
border: none;
border: 1px solid var(--accent, #9f0);
color: #111;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
padding: 8px 18px;
border-radius: 8px;
font-size: 0.84rem;
font-weight: 700;
cursor: pointer;
font-size: 13px;
transition: opacity 0.15s;
transition: all 0.18s ease;
}
.info-title-save-btn:hover {
opacity: 0.85;
.btn-infobox-close:hover {
opacity: 0.9;
transform: translateY(-1px);
}
.info-title-save-btn:disabled {
opacity: 0.5;
cursor: wait;
/* Mobile Responsive */
@media (max-width: 540px) {
.infobox-cards-grid {
grid-template-columns: 1fr;
}
.info-title-status {
font-size: 0.8em;
margin-top: 3px;
display: block;
}
/* =============================================

View File

@@ -11354,7 +11354,7 @@ document.addEventListener('click', (e) => {
return;
}
const closeBtn = e.target.closest('#info-modal-close');
const closeBtn = e.target.closest('#info-modal-close, #info-modal-close-icon');
if (closeBtn) {
e.preventDefault();
const modal = document.getElementById('info-modal');
@@ -11365,6 +11365,21 @@ document.addEventListener('click', (e) => {
return;
}
const copyHashBtn = e.target.closest('#info-copy-hash-btn');
if (copyHashBtn) {
e.preventDefault();
const hash = copyHashBtn.dataset.hash;
if (hash && navigator.clipboard) {
navigator.clipboard.writeText(hash).then(() => {
const origText = copyHashBtn.innerHTML;
copyHashBtn.innerHTML = '<i class="fa-solid fa-check"></i> Copied!';
setTimeout(() => { copyHashBtn.innerHTML = origText; }, 2000);
}).catch(err => console.error('Copy failed', err));
}
return;
}
const visCancelBtn = e.target.closest('#visibility-modal-cancel');
if (visCancelBtn || e.target.id === 'visibility-modal') {
const modal = document.getElementById('visibility-modal');

View File

@@ -236,138 +236,9 @@
</div>
<div id="info-modal" class="modal-overlay" style="display: none;">
<div class="modal-content" style="max-width: 500px;">
<div class="modal-body" style="padding: 20px 0; text-align: left;">
<table class="info-table">
@if(enable_item_title && can_manage_item)
<tr class="info-title-row">
<th>Title</th>
<td>
<div class="info-title-edit-wrap">
<input type="text" id="info-title-input" class="info-title-input" value="{!! item.title || '' !!}" placeholder="Add a title…" maxlength="500" data-item-id="{{ item.id }}" />
<button id="info-title-save" class="info-title-save-btn"><i class="fa-solid fa-check"></i></button>
</div>
<span id="info-title-status" class="info-title-status" style="display:none"></span>
</td>
</tr>
@elseif(enable_item_title && item.title)
<tr>
<th>Title</th>
<td>{!! item.title !!}</td>
</tr>
@endif
<tr class="info-id-row">
<th>ID</th>
<td><code>{{ item.id }}</code></td>
</tr>
<tr class="info-visibility-row">
<th>Visibility</th>
<td>
<span id="info-visibility-label" style="display: inline-flex; align-items: center; gap: 6px;">
@if(item.visibility === 2)
<i class="fa-solid fa-lock" style="color: var(--color-danger, #ff4444);"></i> Private
@elseif(item.visibility === 1)
<i class="fa-solid fa-link" style="color: var(--color-warning, #ffbb33);"></i> Unlisted
@else
<i class="fa-solid fa-globe" style="color: var(--color-success, #00C851);"></i> Public
@endif
</span>
@if(can_manage_item)
<button id="info-visibility-edit-btn" class="btn-secondary btn-sm" style="margin-left: 8px; padding: 2px 8px; font-size: 0.8em;" data-item-id="{{ item.id }}" data-visibility="{{ item.visibility || 0 }}"><i class="fa-solid fa-pen"></i> Edit</button>
@endif
</td>
</tr>
@if(enable_expiring_uploads !== false)
<tr class="info-expiry-row">
<th>Expires</th>
<td>
<div class="info-expiry-wrap" style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
<span id="info-expiry-label" style="display: inline-flex; align-items: center; gap: 6px; color: #ffaa00;" title="{{ item.expires_at ? new Date(item.expires_at * 1000).toLocaleString() : '' }}">
@if(item.expires_at)
<i class="fa-solid fa-clock"></i> {{ item.expires_in || ('in ' + new Date(item.expires_at * 1000).toLocaleString()) }}
@else
<i class="fa-solid fa-clock-slash" style="color: var(--text-muted, #888);"></i> Never
@endif
</span>
@if(can_manage_item)
<div class="info-expiry-edit-controls" style="display: inline-flex; align-items: center; gap: 6px;">
<select id="info-expiry-select" class="info-expiry-select" style="padding: 2px 6px; font-size: 0.85em; background: rgba(0,0,0,0.4); color: #fff; border: 1px solid rgba(255,255,255,0.2); border-radius: 4px;" data-item-id="{{ item.id }}">
<option value="permanent" {{ !item.expires_at ? 'selected' : '' }}>Permanent (Never)</option>
<option value="30minutes">30 Minutes</option>
<option value="1hour">1 Hour</option>
<option value="24hours">24 Hours</option>
<option value="1week">1 Week</option>
<option value="1month">1 Month</option>
</select>
<button id="info-set-expiry-btn" class="btn-secondary btn-sm" style="padding: 2px 8px; font-size: 0.8em;" data-item-id="{{ item.id }}"><i class="fa-solid fa-check"></i> Save</button>
</div>
@endif
</div>
</td>
</tr>
@endif
@include(snippets/info-modal)
<tr>
<th>{{ t('info_modal.file_size') || 'File Size' }}</th>
<td>{{ item.size }}</td>
</tr>
@if(item_has_dimensions)
<tr>
<th>{{ t('info_modal.dimensions') || 'Dimensions' }}</th>
<td>{{ item.width }} × {{ item.height }}</td>
</tr>
@endif
<tr>
<th>{{ t('info_modal.mime_type') || 'MIME Type' }}</th>
<td><code>{{ item.mime }}</code></td>
</tr>
@if(item.checksum)
<tr>
<th>{{ t('info_modal.sha256') || 'SHA-256 Hash' }}</th>
<td><pre class="info-hash-codeblock"><code style="word-break: break-all;">{{ item.checksum.split('_bypass_')[0] }}</code></pre></td>
</tr>
@endif
@if(item.show_repost_row)
<tr class="info-repost-row">
<th>Repost</th>
<td>
@each(item.reposts as rp)
@if(rp.match_type === 'phash')
<a href="/{{ rp.slug || rp.id }}" style="margin-right: 4px; opacity: 0.75;" tooltip="Visually similar (perceptual hash)" flow="up">~#{{ rp.slug || rp.id }}</a>
@else
<a href="/{{ rp.slug || rp.id }}" style="margin-right: 4px;" tooltip="Exact duplicate (checksum)" flow="up">#{{ rp.slug || rp.id }}</a>
@endif
@endeach
</td>
</tr>
@endif
<tr>
<th>{{ t('info_modal.direct_url') || 'Direct URL' }}</th>
<td>
<a href="{{ item.mime === 'video/youtube' ? 'https://www.youtube.com/watch?v=' + item.dest.replace('yt:', '') : item.dest }}" target="_blank">
{{ t('info_modal.view_file') || 'View File' }}
</a>
</td>
</tr>
@if(item.src.short)
<tr>
<th>{{ t('info_modal.source') || 'Source' }}</th>
<td><a href="{{ item.src.long }}" target="_blank" style="word-break: break-all;">{{ item.src.short }}</a></td>
</tr>
@endif
</table>
</div>
<div class="modal-actions" style="display: flex; justify-content: flex-end; gap: 10px;">
@if(can_manage_item)
<button class="btn-secondary" id="info-rethumb-btn" data-item-id="{{ item.id }}"><i class="fa-solid fa-arrows-rotate"></i> Regenerate Thumbnail</button>
@endif
<button class="btn-secondary" id="info-modal-close">{{ t('common.close') || 'Close' }}</button>
</div>
</div>
</div>
<div id="visibility-modal" class="modal-overlay" style="display: none;">
<div class="modal-content" style="max-width: 420px;">
<div class="modal-header" style="margin-bottom: 15px; border-bottom: 1px solid var(--border-color, #333); padding-bottom: 10px;">

View File

@@ -181,138 +181,9 @@
</div>
<div id="info-modal" class="modal-overlay" style="display: none;">
<div class="modal-content" style="max-width: 500px;">
<div class="modal-body" style="padding: 20px 0; text-align: left;">
<table class="info-table">
@if(enable_item_title && can_manage_item)
<tr class="info-title-row">
<th>Title</th>
<td>
<div class="info-title-edit-wrap">
<input type="text" id="info-title-input" class="info-title-input" value="{!! item.title || '' !!}" placeholder="Add a title…" maxlength="500" data-item-id="{{ item.id }}" />
<button id="info-title-save" class="info-title-save-btn"><i class="fa-solid fa-check"></i></button>
</div>
<span id="info-title-status" class="info-title-status" style="display:none"></span>
</td>
</tr>
@elseif(enable_item_title && item.title)
<tr>
<th>Title</th>
<td>{!! item.title !!}</td>
</tr>
@endif
<tr class="info-id-row">
<th>ID</th>
<td><code>{{ item.id }}</code></td>
</tr>
<tr class="info-visibility-row">
<th>Visibility</th>
<td>
<span id="info-visibility-label" style="display: inline-flex; align-items: center; gap: 6px;">
@if(item.visibility === 2)
<i class="fa-solid fa-lock" style="color: var(--color-danger, #ff4444);"></i> Private
@elseif(item.visibility === 1)
<i class="fa-solid fa-link" style="color: var(--color-warning, #ffbb33);"></i> Unlisted
@else
<i class="fa-solid fa-globe" style="color: var(--color-success, #00C851);"></i> Public
@endif
</span>
@if(can_manage_item)
<button id="info-visibility-edit-btn" class="btn-secondary btn-sm" style="margin-left: 8px; padding: 2px 8px; font-size: 0.8em;" data-item-id="{{ item.id }}" data-visibility="{{ item.visibility || 0 }}"><i class="fa-solid fa-pen"></i> Edit</button>
@endif
</td>
</tr>
@if(enable_expiring_uploads !== false)
<tr class="info-expiry-row">
<th>Expires</th>
<td>
<div class="info-expiry-wrap" style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
<span id="info-expiry-label" style="display: inline-flex; align-items: center; gap: 6px; color: #ffaa00;" title="{{ item.expires_at ? new Date(item.expires_at * 1000).toLocaleString() : '' }}">
@if(item.expires_at)
<i class="fa-solid fa-clock"></i> {{ item.expires_in || ('in ' + new Date(item.expires_at * 1000).toLocaleString()) }}
@else
<i class="fa-solid fa-clock-slash" style="color: var(--text-muted, #888);"></i> Never
@endif
</span>
@if(can_manage_item)
<div class="info-expiry-edit-controls" style="display: inline-flex; align-items: center; gap: 6px;">
<select id="info-expiry-select" class="info-expiry-select" style="padding: 2px 6px; font-size: 0.85em; background: rgba(0,0,0,0.4); color: #fff; border: 1px solid rgba(255,255,255,0.2); border-radius: 4px;" data-item-id="{{ item.id }}">
<option value="permanent" {{ !item.expires_at ? 'selected' : '' }}>Permanent (Never)</option>
<option value="30minutes">30 Minutes</option>
<option value="1hour">1 Hour</option>
<option value="24hours">24 Hours</option>
<option value="1week">1 Week</option>
<option value="1month">1 Month</option>
</select>
<button id="info-set-expiry-btn" class="btn-secondary btn-sm" style="padding: 2px 8px; font-size: 0.8em;" data-item-id="{{ item.id }}"><i class="fa-solid fa-check"></i> Save</button>
</div>
@endif
</div>
</td>
</tr>
@endif
@include(snippets/info-modal)
<tr>
<th>{{ t('info_modal.file_size') || 'File Size' }}</th>
<td>{{ item.size }}</td>
</tr>
@if(item_has_dimensions)
<tr>
<th>{{ t('info_modal.dimensions') || 'Dimensions' }}</th>
<td>{{ item.width }} × {{ item.height }}</td>
</tr>
@endif
<tr>
<th>{{ t('info_modal.mime_type') || 'MIME Type' }}</th>
<td><code>{{ item.mime }}</code></td>
</tr>
@if(item.checksum)
<tr>
<th>{{ t('info_modal.sha256') || 'SHA-256 Hash' }}</th>
<td><pre class="info-hash-codeblock"><code style="word-break: break-all;">{{ item.checksum.split('_bypass_')[0] }}</code></pre></td>
</tr>
@endif
@if(item.show_repost_row)
<tr class="info-repost-row">
<th>Repost</th>
<td>
@each(item.reposts as rp)
@if(rp.match_type === 'phash')
<a href="/{{ rp.slug || rp.id }}" style="margin-right: 4px; opacity: 0.75;" tooltip="Visually similar (perceptual hash)" flow="up">~#{{ rp.slug || rp.id }}</a>
@else
<a href="/{{ rp.slug || rp.id }}" style="margin-right: 4px;" tooltip="Exact duplicate (checksum)" flow="up">#{{ rp.slug || rp.id }}</a>
@endif
@endeach
</td>
</tr>
@endif
<tr>
<th>{{ t('info_modal.direct_url') || 'Direct URL' }}</th>
<td>
<a href="{{ item.mime === 'video/youtube' ? 'https://www.youtube.com/watch?v=' + item.dest.replace('yt:', '') : item.dest }}" target="_blank">
{{ t('info_modal.view_file') || 'View File' }}
</a>
</td>
</tr>
@if(item.src.short)
<tr>
<th>{{ t('info_modal.source') || 'Source' }}</th>
<td><a href="{{ item.src.long }}" target="_blank" style="word-break: break-all;">{{ item.src.short }}</a></td>
</tr>
@endif
</table>
</div>
<div class="modal-actions" style="display: flex; justify-content: flex-end; gap: 10px;">
@if(can_manage_item)
<button class="btn-secondary" id="info-rethumb-btn" data-item-id="{{ item.id }}"><i class="fa-solid fa-arrows-rotate"></i> Regenerate Thumbnail</button>
@endif
<button class="btn-secondary" id="info-modal-close">{{ t('common.close') || 'Close' }}</button>
</div>
</div>
</div>
<div id="visibility-modal" class="modal-overlay" style="display: none;">
<div class="modal-content" style="max-width: 420px;">
<div class="modal-header" style="margin-bottom: 15px; border-bottom: 1px solid var(--border-color, #333); padding-bottom: 10px;">

View File

@@ -0,0 +1,188 @@
<div id="info-modal" class="modal-overlay" style="display: none;">
<div class="modal-content infobox-modal-content">
<!-- Header -->
<div class="infobox-modal-header">
<div class="infobox-header-title">
<div class="infobox-icon-badge">
<i class="fa-solid fa-circle-info"></i>
</div>
<div class="infobox-header-text">
<h3>{{ t('info_modal.title') || 'Post & File Details' }}</h3>
<span class="infobox-sub-heading">ID: <code>{{ item.id }}</code></span>
</div>
</div>
<button type="button" class="infobox-close-icon-btn" id="info-modal-close-icon" title="{{ t('common.close') || 'Close' }}">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
<!-- Body -->
<div class="infobox-modal-body">
<!-- Section 1: Post Details -->
<div class="infobox-section">
<div class="infobox-section-header">
<i class="fa-solid fa-sliders"></i>
<span>Post Configuration</span>
</div>
<div class="infobox-cards-grid">
@if(enable_item_title)
<div class="infobox-card full-width">
<label class="infobox-card-label"><i class="fa-solid fa-heading"></i> Title</label>
@if(can_manage_item)
<div class="info-title-edit-wrap">
<input type="text" id="info-title-input" class="info-title-input" value="{!! item.title || '' !!}" placeholder="Add a title…" maxlength="500" data-item-id="{{ item.id }}" />
<button type="button" id="info-title-save" class="info-title-save-btn" title="Save Title"><i class="fa-solid fa-check"></i></button>
</div>
<span id="info-title-status" class="info-title-status" style="display:none"></span>
@else
<div class="infobox-card-value">{!! item.title || '<span class="text-muted">No title set</span>' !!}</div>
@endif
</div>
@endif
<div class="infobox-card">
<label class="infobox-card-label"><i class="fa-solid fa-eye"></i> Visibility</label>
<div class="infobox-card-value-row">
<span id="info-visibility-label" class="visibility-badge vis-{{ item.visibility || 0 }}">
@if(item.visibility === 2)
<i class="fa-solid fa-lock" style="color: var(--color-danger, #ff4444);"></i> Private
@elseif(item.visibility === 1)
<i class="fa-solid fa-link" style="color: var(--color-warning, #ffbb33);"></i> Unlisted
@else
<i class="fa-solid fa-globe" style="color: var(--color-success, #00C851);"></i> Public
@endif
</span>
@if(can_manage_item)
<button type="button" id="info-visibility-edit-btn" class="infobox-action-btn" data-item-id="{{ item.id }}" data-visibility="{{ item.visibility || 0 }}">
<i class="fa-solid fa-pen"></i> Edit
</button>
@endif
</div>
</div>
@if(enable_expiring_uploads !== false)
<div class="infobox-card">
<label class="infobox-card-label"><i class="fa-solid fa-hourglass-half"></i> Expiration</label>
<div class="infobox-expiry-content">
<span id="info-expiry-label" class="expiry-status-label" title="{{ item.expires_at ? new Date(item.expires_at * 1000).toLocaleString() : '' }}">
@if(item.expires_at)
<i class="fa-solid fa-clock" style="color: #ffaa00;"></i> {{ item.expires_in || ('in ' + new Date(item.expires_at * 1000).toLocaleString()) }}
@else
<i class="fa-solid fa-clock-slash" style="color: var(--text-muted, #888);"></i> Permanent
@endif
</span>
@if(can_manage_item)
<div class="info-expiry-edit-controls">
<select id="info-expiry-select" class="infobox-select" data-item-id="{{ item.id }}">
<option value="permanent" {{ !item.expires_at ? 'selected' : '' }}>Permanent</option>
<option value="30minutes">30 Minutes</option>
<option value="1hour">1 Hour</option>
<option value="24hours">24 Hours</option>
<option value="1week">1 Week</option>
<option value="1month">1 Month</option>
</select>
<button type="button" id="info-set-expiry-btn" class="infobox-action-btn" data-item-id="{{ item.id }}" title="Save Expiration">
<i class="fa-solid fa-check"></i>
</button>
</div>
@endif
</div>
</div>
@endif
</div>
</div>
<!-- Section 2: Technical File Specifications -->
<div class="infobox-section">
<div class="infobox-section-header">
<i class="fa-solid fa-file-code"></i>
<span>Technical Specifications</span>
</div>
<div class="infobox-cards-grid">
<div class="infobox-card">
<label class="infobox-card-label"><i class="fa-solid fa-hard-drive"></i> File Size</label>
<div class="infobox-card-value font-mono">{{ item.size }}</div>
</div>
@if(item_has_dimensions)
<div class="infobox-card">
<label class="infobox-card-label"><i class="fa-solid fa-ruler-combined"></i> Dimensions</label>
<div class="infobox-card-value font-mono">{{ item.width }} × {{ item.height }} px</div>
</div>
@endif
<div class="infobox-card">
<label class="infobox-card-label"><i class="fa-solid fa-file-lines"></i> MIME Type</label>
<div class="infobox-card-value"><code class="mime-pill">{{ item.mime }}</code></div>
</div>
<div class="infobox-card">
<label class="infobox-card-label"><i class="fa-solid fa-arrow-up-right-from-square"></i> Direct Link</label>
<div class="infobox-card-value">
<a href="{{ item.mime === 'video/youtube' ? 'https://www.youtube.com/watch?v=' + item.dest.replace('yt:', '') : item.dest }}" target="_blank" class="infobox-link-pill">
<i class="fa-solid fa-up-right-from-square"></i> {{ t('info_modal.view_file') || 'Open File' }}
</a>
</div>
</div>
@if(item.src && item.src.short)
<div class="infobox-card full-width">
<label class="infobox-card-label"><i class="fa-solid fa-link"></i> Source URL</label>
<div class="infobox-card-value">
<a href="{{ item.src.long }}" target="_blank" class="infobox-source-link">
<i class="fa-solid fa-globe"></i> {{ item.src.short }}
</a>
</div>
</div>
@endif
@if(item.checksum)
<div class="infobox-card full-width">
<div class="infobox-card-label-row">
<label class="infobox-card-label"><i class="fa-solid fa-fingerprint"></i> SHA-256 Hash</label>
<button type="button" class="copy-hash-btn" id="info-copy-hash-btn" data-hash="{{ item.checksum.split('_bypass_')[0] }}" title="Copy SHA-256 Hash">
<i class="fa-solid fa-copy"></i> Copy
</button>
</div>
<div class="info-hash-codeblock font-mono">
<code>{{ item.checksum.split('_bypass_')[0] }}</code>
</div>
</div>
@endif
</div>
</div>
<!-- Section 3: Reposts & Duplicates (if present) -->
@if(item.show_repost_row)
<div class="infobox-section">
<div class="infobox-section-header">
<i class="fa-solid fa-copy"></i>
<span>Similar & Duplicate Uploads</span>
</div>
<div class="infobox-reposts-list">
@each(item.reposts as rp)
<a href="/{{ rp.slug || rp.id }}" class="repost-pill {{ rp.match_type === 'phash' ? 'phash' : 'exact' }}" tooltip="{{ rp.match_type === 'phash' ? 'Visually similar (perceptual hash)' : 'Exact duplicate (checksum)' }}" flow="up">
<i class="fa-solid {{ rp.match_type === 'phash' ? 'fa-clone' : 'fa-copy' }}"></i>
<span>{{ rp.match_type === 'phash' ? '~#' : '#' }}{{ rp.slug || rp.id }}</span>
</a>
@endeach
</div>
</div>
@endif
</div>
<!-- Footer -->
<div class="infobox-modal-footer">
@if(can_manage_item)
<button type="button" class="btn-infobox-action" id="info-rethumb-btn" data-item-id="{{ item.id }}">
<i class="fa-solid fa-arrows-rotate"></i> Regenerate Thumbnail
</button>
@endif
<button type="button" class="btn-infobox-close" id="info-modal-close">{{ t('common.close') || 'Close' }}</button>
</div>
</div>
</div>