diff --git a/shaarli-pro/css/style.css b/shaarli-pro/css/style.css index 2698e50..9e908a5 100644 --- a/shaarli-pro/css/style.css +++ b/shaarli-pro/css/style.css @@ -28,7 +28,7 @@ --header-bg: #3b82f6; --header-text: #ffffff; - --sidebar-width: 260px; + --sidebar-width: 230px; --sidebar-collapsed: 60px; --header-height: 56px; @@ -100,6 +100,8 @@ html { scroll-behavior: smooth; + font-size: 14.5px; + /* Decrease global scale */ } body { @@ -1001,9 +1003,9 @@ input:checked+.theme-slider:before { /* ===== Content Container ===== */ .content-container { flex: 1; - padding: 1.5rem; - max-width: 1400px; - margin: 0 auto; + padding: 1rem 1.5rem; + max-width: 100%; + margin: 0; width: 100%; } @@ -1090,6 +1092,8 @@ input:checked+.theme-slider:before { transition: all 0.2s ease; position: relative; overflow: hidden; + content-visibility: auto; + contain-intrinsic-size: auto 300px; } /* Link card hover states - same for both public and private */ @@ -1137,7 +1141,8 @@ input:checked+.theme-slider:before { } .view-grid .link-select-checkbox { - right: 3.5rem; + right: auto; + left: 0.75rem; } [data-theme="dark"] .link-select-checkbox { @@ -1330,6 +1335,8 @@ input:checked+.theme-slider:before { .link-footer { display: flex; + flex-wrap: wrap; + gap: 0.5rem; align-items: center; justify-content: space-between; margin-top: 1rem; @@ -1348,6 +1355,8 @@ input:checked+.theme-slider:before { display: flex; flex-wrap: wrap; gap: 0.375rem; + flex: 1 1 auto; + min-width: 0; } .link-tag a { @@ -1375,6 +1384,11 @@ input:checked+.theme-slider:before { .link-actions { display: flex; gap: 0.25rem; + flex-shrink: 0; + align-items: center; + flex-wrap: nowrap; + margin-left: auto; + /* Force alignment to the right */ } .link-actions a, @@ -2375,6 +2389,10 @@ select:focus { box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1); } +.picwall-pictureframe { + will-change: transform; +} + .picwall-pictureframe:hover { transform: translateY(-4px) scale(1.02); box-shadow: var(--shadow-xl); @@ -2578,6 +2596,8 @@ select:focus { margin-bottom: 0; box-shadow: none; transition: background 0.15s ease; + position: relative; + /* Ensure absolute positioning works */ } .view-compact .link-outer:first-child { @@ -2613,6 +2633,9 @@ select:focus { order: -1; margin-left: -1.5rem; margin-right: 0.5rem; + align-self: flex-start; + /* Move to top */ + margin-top: 0.125rem; } .view-compact .link-visibility-badge i { @@ -2674,6 +2697,8 @@ select:focus { display: flex; align-items: center; gap: 0.75rem; + width: 100%; + /* Force width to constrain tags */ } .view-compact .link-tag-list { @@ -3043,51 +3068,37 @@ select:focus { /* ===== Plugin Zone Styling ===== */ -/* Plugin buttons injected into paging (e.g., "Mark as Read") */ +/* Hide readitlater "Mark as Read" button from paging area */ .paging-plugin { - display: inline-flex; - align-items: center; + display: none; } -.paging-plugin a { - display: inline-flex; - align-items: center; - gap: 0.375rem; - padding: 0.375rem 0.75rem; - border-radius: 0.375rem; - font-size: 0.8rem; - font-weight: 500; - color: var(--text-secondary); - background: var(--bg-card); - border: 1px solid var(--border); - transition: all 0.2s ease; - text-decoration: none; - white-space: nowrap; -} - -.paging-plugin a:hover { - background: var(--primary-light); - color: var(--primary); - border-color: var(--primary); -} - -/* Plugin zone inside link cards */ +/* Plugin zone inside link cards - inline with actions */ .link-plugin { - display: flex; - align-items: center; - gap: 0.25rem; - margin-top: 0.25rem; + display: contents; } .link-plugin a { display: inline-flex; align-items: center; - gap: 0.25rem; - padding: 0.25rem 0.625rem; + justify-content: center; + width: 36px; + height: 36px; + background: transparent; + border: none; border-radius: 0.375rem; - font-size: 0.75rem; color: var(--text-muted); - transition: all 0.2s ease; + cursor: pointer; + transition: all 0.15s ease; + text-decoration: none; + padding: 0; + white-space: nowrap; + overflow: hidden; + font-size: 0; +} + +.link-plugin a i { + font-size: 1.15rem; } .link-plugin a:hover { @@ -3095,6 +3106,208 @@ select:focus { color: var(--primary); } +/* QR code plugin wrapper */ +.link-plugin .linkqrcode { + display: contents; +} + +.link-plugin a img { + width: 18px; + height: 18px; + opacity: 0.5; + transition: opacity 0.15s ease; + filter: var(--plugin-icon-filter, none); +} + +[data-theme="dark"] .link-plugin a img { + filter: invert(1) brightness(0.6); +} + +.link-plugin a:hover img { + opacity: 0.9; +} + +/* ===== ReadItLater Plugin Integration ===== */ +/* The readitlater toggle link */ +.link-plugin .readitlater-toggle { + display: inline-flex !important; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + font-size: 0 !important; + color: var(--text-muted); +} + +.link-plugin .readitlater-toggle:hover { + background: rgba(239, 68, 68, 0.1); + color: #ef4444; +} + +.link-plugin .readitlater-toggle .readitlater-icon { + font-size: 0; + display: contents; +} + +.link-plugin .readitlater-toggle .readitlater-icon i { + font-size: 1.15rem; +} + +/* "To Read" badge on the card - grid/list views */ +.readitlater-badge { + position: absolute; + top: 0.75rem; + right: 3.25rem; + display: inline-flex; + align-items: center; + gap: 0.25rem; + padding: 0.25rem 0.5rem; + background: rgba(239, 68, 68, 0.12); + border: 1px solid rgba(239, 68, 68, 0.3); + border-radius: 0.375rem; + font-size: 0.65rem; + font-weight: 700; + color: #ef4444; + z-index: 11; + white-space: nowrap; + text-transform: uppercase; + letter-spacing: 0.04em; + line-height: 1; +} + +.readitlater-badge i { + font-size: 0.8rem; + line-height: 1; +} + +[data-theme="dark"] .readitlater-badge { + background: rgba(239, 68, 68, 0.18); + border-color: rgba(239, 68, 68, 0.4); + color: #f87171; +} + +/* List view - badge next to visibility badge */ +.view-list .readitlater-badge { + right: 4rem; + top: 1.25rem; +} + +/* Compact view - badge absolute top-right */ +.view-compact .readitlater-badge { + position: absolute; + top: 0.5rem; + right: 0.5rem; + left: auto; + bottom: auto; + margin: 0; + align-self: auto; + order: unset; +} + +/* Red accent border for unread bookmarks */ +.link-outer.readitlater-unread { + border-left: 3px solid #ef4444; +} + +/* Unread eye icon color - red tint */ +.readitlater-unread .readitlater-toggle { + color: #ef4444 !important; +} + +.readitlater-unread .readitlater-toggle:hover { + background: rgba(239, 68, 68, 0.15) !important; +} + +/* Hide default QR code inline popup (we use our modal instead) */ +#permalinkQrcode { + display: none !important; +} + +/* QR Code Modal */ +.qrcode-modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.75); + backdrop-filter: blur(4px); + z-index: 1100; + display: none; + align-items: center; + justify-content: center; + padding: 2rem; + opacity: 0; + transition: opacity 0.3s ease; +} + +.qrcode-modal-overlay.show { + display: flex; + opacity: 1; +} + +.qrcode-modal-content { + background: var(--bg-card); + padding: 2rem; + border-radius: 1rem; + box-shadow: var(--shadow-xl); + border: 1px solid var(--border); + position: relative; + max-width: 360px; + width: 100%; + text-align: center; + transform: translateY(20px) scale(0.95); + transition: transform 0.3s ease; +} + +.qrcode-modal-overlay.show .qrcode-modal-content { + transform: translateY(0) scale(1); +} + +.qrcode-modal-close { + position: absolute; + top: 0.75rem; + right: 0.75rem; + width: 32px; + height: 32px; + border-radius: 50%; + background: var(--bg-body); + border: 1px solid var(--border); + color: var(--text-main); + font-size: 1.25rem; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s ease; +} + +.qrcode-modal-close:hover { + background: var(--danger); + color: white; + border-color: var(--danger); + transform: rotate(90deg); +} + +.qrcode-modal-content img { + display: block; + max-width: 280px; + width: 100%; + height: auto; + margin: 0 auto; + border-radius: 0.75rem; + background: white; + padding: 1.25rem; + box-sizing: border-box; +} + +.qrcode-modal-title { + margin-top: 1rem; + font-size: 0.85rem; + color: var(--text-secondary); + word-break: break-all; +} + /* Single page pagination - centered stats only */ .paging.single-page { justify-content: center; @@ -3217,4 +3430,242 @@ select:focus { /* Button style adjustment if needed */ .view-desc-btn { cursor: pointer; +} + +/* ===== Persistent Media Player ===== */ +.media-player-bar { + position: fixed; + bottom: 0; + left: var(--sidebar-width); + right: 0; + background: var(--bg-card); + border-top: 1px solid var(--border); + box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15); + z-index: 200; + transform: translateY(100%); + transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1); + backdrop-filter: blur(12px); +} + +.media-player-bar.show { + transform: translateY(0); +} + +.media-player-inner { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.625rem 1.25rem; + max-width: 1400px; + margin: 0 auto; +} + +/* Play/Pause Button */ +.media-player-btn { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + background: var(--primary); + color: white; + border: none; + cursor: pointer; + transition: all 0.2s ease; + flex-shrink: 0; +} + +.media-player-btn:hover { + background: var(--primary-hover); + transform: scale(1.05); +} + +.media-player-btn i { + font-size: 1.25rem; +} + +.media-player-btn-sm { + width: 32px; + height: 32px; + background: transparent; + color: var(--text-muted); + border-radius: 0.375rem; +} + +.media-player-btn-sm:hover { + background: var(--primary-light); + color: var(--primary); + transform: none; +} + +.media-player-btn-sm i { + font-size: 1.1rem; +} + +/* Info Section */ +.media-player-info { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.media-player-title { + font-size: 0.85rem; + font-weight: 500; + color: var(--text-main); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 400px; +} + +/* Progress Bar */ +.media-player-progress-wrap { + width: 100%; +} + +.media-player-progress { + -webkit-appearance: none; + appearance: none; + width: 100%; + height: 4px; + background: var(--border); + border-radius: 2px; + outline: none; + cursor: pointer; + transition: height 0.15s ease; +} + +.media-player-progress:hover { + height: 6px; +} + +.media-player-progress::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--primary); + cursor: pointer; + border: 2px solid white; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); +} + +.media-player-progress::-moz-range-thumb { + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--primary); + cursor: pointer; + border: 2px solid white; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); +} + +/* Time Display */ +.media-player-time { + font-size: 0.75rem; + color: var(--text-muted); + white-space: nowrap; + flex-shrink: 0; + font-variant-numeric: tabular-nums; + min-width: 90px; + text-align: center; +} + +/* Volume */ +.media-player-volume-wrap { + display: flex; + align-items: center; + gap: 0.25rem; + flex-shrink: 0; +} + +.media-player-volume { + -webkit-appearance: none; + appearance: none; + width: 80px; + height: 4px; + background: var(--border); + border-radius: 2px; + outline: none; + cursor: pointer; +} + +.media-player-volume::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--primary); + cursor: pointer; +} + +.media-player-volume::-moz-range-thumb { + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--primary); + cursor: pointer; +} + +/* Close Button */ +.media-player-close:hover { + background: rgba(239, 68, 68, 0.1) !important; + color: var(--danger) !important; +} + +/* Play button injected into bookmark action bars */ +.media-play-action { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + background: transparent; + border: none; + border-radius: 0.375rem; + color: var(--primary); + cursor: pointer; + transition: all 0.15s ease; + text-decoration: none; +} + +.media-play-action:hover { + background: var(--primary-light); + color: var(--primary-hover); +} + +.media-play-action i { + font-size: 1.15rem; +} + +/* Responsive adjustments for media player */ +@media (max-width: 1024px) { + .media-player-bar { + left: 0; + } +} + +@media (max-width: 768px) { + .media-player-inner { + gap: 0.5rem; + padding: 0.5rem 1rem; + } + + .media-player-title { + max-width: 150px; + } + + .media-player-volume-wrap { + display: none; + } + + .media-player-time { + display: none; + } } \ No newline at end of file diff --git a/shaarli-pro/js/script.js b/shaarli-pro/js/script.js index df65355..fe76c00 100644 --- a/shaarli-pro/js/script.js +++ b/shaarli-pro/js/script.js @@ -995,4 +995,382 @@ document.addEventListener('DOMContentLoaded', () => { } } }); + + // ===== QR Code Plugin Modal ===== + const qrcodeModal = document.getElementById('qrcode-modal'); + const qrcodeModalBody = document.getElementById('qrcode-modal-body'); + const qrcodeModalClose = document.getElementById('qrcode-modal-close'); + + function openQrcodeModal(permalink, title) { + if (!qrcodeModal || !qrcodeModalBody) return; + + // Show loading state + qrcodeModalBody.innerHTML = ` +