diff --git a/shaarli-pro/css/custom_views.css b/shaarli-pro/css/custom_views.css index 6f75207..959c586 100644 --- a/shaarli-pro/css/custom_views.css +++ b/shaarli-pro/css/custom_views.css @@ -380,8 +380,9 @@ body.view-notes .content-container { /* Prevent split */ position: relative; transition: box-shadow 0.2s, transform 0.2s, background-color 0.2s; - overflow: hidden; - /* for cover image */ + overflow: visible; + /* allow palette popup to escape card bounds */ + color: var(--note-card-fg, #202124); } /* Dark Mode Card */ @@ -408,6 +409,11 @@ body.view-notes .content-container { object-fit: cover; } +.note-cover { + overflow: hidden; + border-radius: 8px 8px 0 0; +} + /* Inner Content */ .note-inner { padding: 12px 16px; @@ -483,6 +489,7 @@ body.view-notes .content-container { transition: opacity 0.2s; position: relative; /* For palette popup */ + z-index: 2; } /* Show actions on hover */ @@ -497,8 +504,9 @@ body.view-notes .content-container { } } -.note-hover-actions button, -.note-hover-actions a { +.note-hover-actions > button, +.note-hover-actions > a, +.note-hover-actions > div > button { background: none; border: none; width: 34px; @@ -515,19 +523,22 @@ body.view-notes .content-container { text-decoration: none; } -[data-theme="dark"] .note-hover-actions button, -[data-theme="dark"] .note-hover-actions a { +[data-theme="dark"] .note-hover-actions > button, +[data-theme="dark"] .note-hover-actions > a, +[data-theme="dark"] .note-hover-actions > div > button { color: #9aa0a6; } -.note-hover-actions button:hover, -.note-hover-actions a:hover { +.note-hover-actions > button:hover, +.note-hover-actions > a:hover, +.note-hover-actions > div > button:hover { background-color: rgba(136, 136, 136, 0.2); color: var(--text-color); } -[data-theme="dark"] .note-hover-actions button:hover, -[data-theme="dark"] .note-hover-actions a:hover { +[data-theme="dark"] .note-hover-actions > button:hover, +[data-theme="dark"] .note-hover-actions > a:hover, +[data-theme="dark"] .note-hover-actions > div > button:hover { color: #e8eaed; } @@ -549,181 +560,497 @@ body.view-notes .content-container { /* Reference: Keep Colors */ /* Default */ .note-card.note-color-default { - background-color: var(--background-secondary, #ffffff); + background-color: #20293a; + border-color: transparent; + --note-card-fg: #dbe7ff; } [data-theme="dark"] .note-card.note-color-default { - background-color: #202124; - border-color: #5f6368; + background-color: #20293a; + border-color: transparent; + --note-card-fg: #dbe7ff; } /* Red */ .note-card.note-color-red { background-color: #f28b82; border-color: transparent; + --note-card-fg: #2f1714; } [data-theme="dark"] .note-card.note-color-red { - background-color: #5c2b29; - border-color: #5c2b29; + background-color: #f28b82; + border-color: transparent; + --note-card-fg: #2f1714; } /* Orange */ .note-card.note-color-orange { background-color: #fbbc04; border-color: transparent; + --note-card-fg: #3d2a00; } [data-theme="dark"] .note-card.note-color-orange { - background-color: #614a19; - border-color: #614a19; + background-color: #fbbc04; + border-color: transparent; + --note-card-fg: #3d2a00; } /* Yellow */ .note-card.note-color-yellow { background-color: #fff475; border-color: transparent; + --note-card-fg: #383100; } [data-theme="dark"] .note-card.note-color-yellow { - background-color: #635d19; - border-color: #635d19; + background-color: #fff475; + border-color: transparent; + --note-card-fg: #383100; } /* Green */ .note-card.note-color-green { background-color: #ccff90; border-color: transparent; + --note-card-fg: #203400; } [data-theme="dark"] .note-card.note-color-green { - background-color: #345920; - border-color: #345920; + background-color: #ccff90; + border-color: transparent; + --note-card-fg: #203400; } /* Teal */ .note-card.note-color-teal { background-color: #a7ffeb; border-color: transparent; + --note-card-fg: #08342d; } [data-theme="dark"] .note-card.note-color-teal { - background-color: #16504b; - border-color: #16504b; + background-color: #a7ffeb; + border-color: transparent; + --note-card-fg: #08342d; } /* Blue */ .note-card.note-color-blue { background-color: #cbf0f8; border-color: transparent; + --note-card-fg: #113541; } [data-theme="dark"] .note-card.note-color-blue { - background-color: #2d555e; - border-color: #2d555e; + background-color: #cbf0f8; + border-color: transparent; + --note-card-fg: #113541; } /* Dark Blue */ .note-card.note-color-darkblue { background-color: #aecbfa; border-color: transparent; + --note-card-fg: #102645; } [data-theme="dark"] .note-card.note-color-darkblue { - background-color: #1e3a5f; - border-color: #1e3a5f; + background-color: #aecbfa; + border-color: transparent; + --note-card-fg: #102645; } /* Purple */ .note-card.note-color-purple { background-color: #d7aefb; border-color: transparent; + --note-card-fg: #2f1845; } [data-theme="dark"] .note-card.note-color-purple { - background-color: #42275e; - border-color: #42275e; + background-color: #d7aefb; + border-color: transparent; + --note-card-fg: #2f1845; } /* Pink */ .note-card.note-color-pink { background-color: #fdcfe8; border-color: transparent; + --note-card-fg: #4a1d34; } [data-theme="dark"] .note-card.note-color-pink { - background-color: #5b2245; - border-color: #5b2245; + background-color: #fdcfe8; + border-color: transparent; + --note-card-fg: #4a1d34; } /* Brown */ .note-card.note-color-brown { background-color: #e6c9a8; border-color: transparent; + --note-card-fg: #3c2714; } [data-theme="dark"] .note-card.note-color-brown { - background-color: #442f19; - border-color: #442f19; + background-color: #e6c9a8; + border-color: transparent; + --note-card-fg: #3c2714; } /* Grey */ .note-card.note-color-grey { background-color: #e8eaed; border-color: transparent; + --note-card-fg: #2a2d31; } [data-theme="dark"] .note-card.note-color-grey { - background-color: #3c3f43; - border-color: #3c3f43; + background-color: #e8eaed; + border-color: transparent; + --note-card-fg: #2a2d31; } +.note-card.note-has-bg, +.note-modal.note-has-bg, +.link-outer.note-has-bg { + background-image: linear-gradient(rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.16)), var(--note-bg-image); + background-size: cover; + background-position: center; +} + +.note-card .note-title, +.note-card .note-body, +.note-card .note-tag, +.note-card .note-hover-actions button, +.note-card .note-hover-actions a { + color: inherit; +} + +.note-card[class*="note-color-"] { + color: var(--note-card-fg, #202124); +} + +.link-outer[class*="note-color-"] { + color: var(--note-card-fg, #202124); +} + +.link-outer.note-color-default { + background-color: #20293a; + border-color: transparent; + --note-card-fg: #dbe7ff; +} + +[data-theme="dark"] .link-outer.note-color-default { + background-color: #20293a; + border-color: transparent; + --note-card-fg: #dbe7ff; +} + +.link-outer.note-color-red { + background-color: #f28b82; + border-color: transparent; + --note-card-fg: #2f1714; +} + +[data-theme="dark"] .link-outer.note-color-red { + background-color: #f28b82; + border-color: transparent; + --note-card-fg: #2f1714; +} + +.link-outer.note-color-orange { + background-color: #fbbc04; + border-color: transparent; + --note-card-fg: #3d2a00; +} + +[data-theme="dark"] .link-outer.note-color-orange { + background-color: #fbbc04; + border-color: transparent; + --note-card-fg: #3d2a00; +} + +.link-outer.note-color-yellow { + background-color: #fff475; + border-color: transparent; + --note-card-fg: #383100; +} + +[data-theme="dark"] .link-outer.note-color-yellow { + background-color: #fff475; + border-color: transparent; + --note-card-fg: #383100; +} + +.link-outer.note-color-green { + background-color: #ccff90; + border-color: transparent; + --note-card-fg: #203400; +} + +[data-theme="dark"] .link-outer.note-color-green { + background-color: #ccff90; + border-color: transparent; + --note-card-fg: #203400; +} + +.link-outer.note-color-teal { + background-color: #a7ffeb; + border-color: transparent; + --note-card-fg: #08342d; +} + +[data-theme="dark"] .link-outer.note-color-teal { + background-color: #a7ffeb; + border-color: transparent; + --note-card-fg: #08342d; +} + +.link-outer.note-color-blue { + background-color: #cbf0f8; + border-color: transparent; + --note-card-fg: #113541; +} + +[data-theme="dark"] .link-outer.note-color-blue { + background-color: #cbf0f8; + border-color: transparent; + --note-card-fg: #113541; +} + +.link-outer.note-color-darkblue { + background-color: #aecbfa; + border-color: transparent; + --note-card-fg: #102645; +} + +[data-theme="dark"] .link-outer.note-color-darkblue { + background-color: #aecbfa; + border-color: transparent; + --note-card-fg: #102645; +} + +.link-outer.note-color-purple { + background-color: #d7aefb; + border-color: transparent; + --note-card-fg: #2f1845; +} + +[data-theme="dark"] .link-outer.note-color-purple { + background-color: #d7aefb; + border-color: transparent; + --note-card-fg: #2f1845; +} + +.link-outer.note-color-pink { + background-color: #fdcfe8; + border-color: transparent; + --note-card-fg: #4a1d34; +} + +[data-theme="dark"] .link-outer.note-color-pink { + background-color: #fdcfe8; + border-color: transparent; + --note-card-fg: #4a1d34; +} + +.link-outer.note-color-brown { + background-color: #e6c9a8; + border-color: transparent; + --note-card-fg: #3c2714; +} + +[data-theme="dark"] .link-outer.note-color-brown { + background-color: #e6c9a8; + border-color: transparent; + --note-card-fg: #3c2714; +} + +.link-outer.note-color-grey { + background-color: #e8eaed; + border-color: transparent; + --note-card-fg: #2a2d31; +} + +[data-theme="dark"] .link-outer.note-color-grey { + background-color: #e8eaed; + border-color: transparent; + --note-card-fg: #2a2d31; +} + +.link-outer[class*="note-color-"] .link-title, +.link-outer[class*="note-color-"] .link-url, +.link-outer[class*="note-color-"] .link-meta, +.link-outer[class*="note-color-"] .link-description, +.link-outer[class*="note-color-"] .link-tag a, +.link-outer[class*="note-color-"] .link-actions a, +.link-outer[class*="note-color-"] .link-actions button, +.link-outer[class*="note-color-"] .bookmark-palette > button { + color: inherit; +} + +.link-actions .bookmark-palette { + display: flex; + align-items: center; +} + +.link-actions .bookmark-palette > button { + background: none; + border: none; + width: 36px; + height: 36px; + border-radius: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + cursor: pointer; +} + +.link-actions .bookmark-palette > button i { + font-size: 1.15rem; +} + +.link-actions .bookmark-palette > button:hover { + background: rgba(255, 255, 255, 0.08); +} + +.link-actions .bookmark-palette .palette-popup { + left: 0; + right: auto; +} /* --- PALETTE POPUP --- */ .palette-popup { position: absolute; bottom: 100%; left: 0; - width: 320px; - background: white; + width: min(500px, 92vw); + background: #1f232b; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); - border-radius: 4px; - padding: 8px; - display: flex; - flex-wrap: wrap; - gap: 4px; - z-index: 100; + border-radius: 12px; + padding: 0; + z-index: 5000; display: none; /* JS toggles this */ } [data-theme="dark"] .palette-popup { - background: #202124; + background: #1f232b; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5); border: 1px solid #5f6368; } .palette-popup.open { - display: flex; + display: block; +} + +.palette-popup.open-down { + top: calc(100% + 8px); + bottom: auto; } .palette-btn { - width: 24px; - height: 24px; + width: 32px; + height: 32px; + min-width: 32px; + min-height: 32px; + padding: 0; border-radius: 50%; - border: 1px solid rgba(0, 0, 0, 0.1); + border: 1px solid rgba(255, 255, 255, 0.18); cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; position: relative; + flex: 0 0 auto; + background-position: center; + background-size: cover; } .palette-btn:hover { - border-color: #999; + border-color: rgba(255, 255, 255, 0.8); } [data-theme="dark"] .palette-btn { border-color: rgba(255, 255, 255, 0.2); } +.palette-btn.is-active { + outline: 2px solid #a855f7; + outline-offset: 1px; +} + +.palette-row { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 12px; + overflow-x: auto; + flex-wrap: nowrap; + scrollbar-width: thin; +} + +.palette-row::-webkit-scrollbar { + height: 6px; +} + +.palette-row::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.24); + border-radius: 999px; +} + +.palette-row+.palette-row { + border-top: 1px solid rgba(255, 255, 255, 0.14); +} + +.palette-btn-default, +.palette-btn-bg-none { + background: #2b313c; + color: #d7dce5; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.palette-btn-default i, +.palette-btn-bg-none i { + font-size: 1rem; +} + +/* Prevent link action generic button rules from deforming swatches in bookmark palette */ +.link-actions .palette-popup .palette-btn { + width: 32px; + height: 32px; + min-width: 32px; + min-height: 32px; + border-radius: 50%; + padding: 0; + background: transparent; + border: 1px solid rgba(255, 255, 255, 0.18); + background-position: center; + background-size: cover; +} + +.link-actions .palette-popup .palette-btn:hover { + background: transparent; + border-color: rgba(255, 255, 255, 0.8); +} + +.link-outer.palette-open, +.view-list .link-outer.palette-open, +.view-compact .link-outer.palette-open { + overflow: visible; + z-index: 4000; + content-visibility: visible; + contain: none; + contain-intrinsic-size: auto; +} + /* --- MODAL FOR NOTE VIEW --- */ .note-modal-overlay { position: fixed; @@ -748,40 +1075,301 @@ body.view-notes .content-container { } .note-modal { - background: var(--background-secondary, #fff); - width: 600px; - max-width: 90%; - max-height: 90vh; - border-radius: 8px; - overflow-y: auto; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); - padding: 24px; + background: var(--modal-note-bg, #7a4b00); + color: var(--modal-note-fg, #fff2d9); + width: min(720px, 92vw); + max-height: 88vh; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.42); position: relative; + display: flex; + flex-direction: column; + --note-scrollbar-thumb: rgba(255, 229, 170, 0.75); + --note-separator: rgba(255, 230, 182, 0.22); + --note-footer-bg: rgba(0, 0, 0, 0.14); +} + +.note-modal.note-color-default { + --modal-note-bg: #20293a; + --modal-note-fg: #dbe7ff; + --note-scrollbar-thumb: rgba(219, 231, 255, 0.45); + --note-separator: rgba(219, 231, 255, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.08); +} + +.note-modal.note-color-red { + --modal-note-bg: #f28b82; + --modal-note-fg: #2f1714; + --note-scrollbar-thumb: rgba(47, 23, 20, 0.45); + --note-separator: rgba(47, 23, 20, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.18); +} + +.note-modal.note-color-orange { + --modal-note-bg: #fbbc04; + --modal-note-fg: #3d2a00; + --note-scrollbar-thumb: rgba(61, 42, 0, 0.45); + --note-separator: rgba(61, 42, 0, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.2); +} + +.note-modal.note-color-yellow { + --modal-note-bg: #fff475; + --modal-note-fg: #383100; + --note-scrollbar-thumb: rgba(56, 49, 0, 0.45); + --note-separator: rgba(56, 49, 0, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.24); +} + +.note-modal.note-color-green { + --modal-note-bg: #ccff90; + --modal-note-fg: #203400; + --note-scrollbar-thumb: rgba(32, 52, 0, 0.45); + --note-separator: rgba(32, 52, 0, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.22); +} + +.note-modal.note-color-teal { + --modal-note-bg: #a7ffeb; + --modal-note-fg: #08342d; + --note-scrollbar-thumb: rgba(8, 52, 45, 0.45); + --note-separator: rgba(8, 52, 45, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.22); +} + +.note-modal.note-color-blue { + --modal-note-bg: #cbf0f8; + --modal-note-fg: #113541; + --note-scrollbar-thumb: rgba(17, 53, 65, 0.45); + --note-separator: rgba(17, 53, 65, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.22); +} + +.note-modal.note-color-darkblue { + --modal-note-bg: #aecbfa; + --modal-note-fg: #102645; + --note-scrollbar-thumb: rgba(16, 38, 69, 0.45); + --note-separator: rgba(16, 38, 69, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.2); +} + +.note-modal.note-color-purple { + --modal-note-bg: #d7aefb; + --modal-note-fg: #2f1845; + --note-scrollbar-thumb: rgba(47, 24, 69, 0.45); + --note-separator: rgba(47, 24, 69, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.2); +} + +.note-modal.note-color-pink { + --modal-note-bg: #fdcfe8; + --modal-note-fg: #4a1d34; + --note-scrollbar-thumb: rgba(74, 29, 52, 0.45); + --note-separator: rgba(74, 29, 52, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.2); +} + +.note-modal.note-color-brown { + --modal-note-bg: #e6c9a8; + --modal-note-fg: #3c2714; + --note-scrollbar-thumb: rgba(60, 39, 20, 0.45); + --note-separator: rgba(60, 39, 20, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.2); +} + +.note-modal.note-color-grey { + --modal-note-bg: #e8eaed; + --modal-note-fg: #2a2d31; + --note-scrollbar-thumb: rgba(42, 45, 49, 0.45); + --note-separator: rgba(42, 45, 49, 0.2); + --note-footer-bg: rgba(255, 255, 255, 0.2); } [data-theme="dark"] .note-modal { - background: var(--bg-card); - border: 1px solid var(--border); + border: 1px solid rgba(255, 255, 255, 0.12); +} + +.note-modal-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + padding: 18px 20px 10px; + flex-shrink: 0; } .note-modal .note-title { - font-size: 1.3rem; - line-height: 1.5; - margin-bottom: 16px; + font-size: 1.6rem; + line-height: 1.35; + margin: 0; + color: inherit; + font-weight: 500; +} + +.note-modal-pin-toggle { + background: transparent; + border: none; + color: inherit; + opacity: 0.85; + width: 34px; + height: 34px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + flex-shrink: 0; + transition: background-color 0.2s, opacity 0.2s; +} + +.note-modal-pin-toggle i { + font-size: 1.2rem; +} + +.note-modal-pin-toggle:hover, +.note-modal-pin-toggle.active { + opacity: 1; + background-color: rgba(255, 255, 255, 0.14); +} + +.note-modal-content { + flex: 1; + overflow-y: auto; + padding: 0 20px 18px; + scrollbar-width: thin; + scrollbar-color: var(--note-scrollbar-thumb) transparent; +} + +.note-modal-content::-webkit-scrollbar { + width: 10px; +} + +.note-modal-content::-webkit-scrollbar-track { + background: transparent; +} + +.note-modal-content::-webkit-scrollbar-thumb { + background: var(--note-scrollbar-thumb); + border-radius: 10px; + border: 2px solid transparent; + background-clip: content-box; } .note-modal .note-body { font-size: 1rem; - line-height: 1.5; - -webkit-line-clamp: unset; - line-clamp: unset; + line-height: 1.75; + display: block; + -webkit-line-clamp: initial; + line-clamp: initial; + -webkit-box-orient: initial; max-height: none; overflow: visible; + color: inherit; } -/* Modal actions at bottom? */ -.note-modal-actions { - margin-top: 24px; +.note-modal .note-body * { + color: inherit; +} + +.note-modal-tags { display: flex; - justify-content: flex-end; + flex-wrap: wrap; + gap: 8px; + padding: 10px 20px 12px; + border-top: 1px solid var(--note-separator); + flex-shrink: 0; +} + +.note-modal-tags.is-empty { + display: none; +} + +.note-modal-tags .note-tag { + background: rgba(255, 255, 255, 0.12); + border: 1px solid rgba(255, 255, 255, 0.22); + color: inherit; + border-radius: 999px; + padding: 4px 10px; + font-size: 0.72rem; + letter-spacing: 0.02em; +} + +.note-modal-actions { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 8px 12px; + border-top: 1px solid var(--note-separator); + background: var(--note-footer-bg); + flex-shrink: 0; +} + +.note-modal-actions-left { + display: flex; + align-items: center; + gap: 2px; + flex-wrap: wrap; +} + +.note-modal-actions-left > button, +.note-modal-actions-left > a, +.note-modal-actions-left > .note-modal-color-picker > button { + background: transparent; + border: none; + color: inherit; + opacity: 0.9; + width: 32px; + height: 32px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + text-decoration: none; + transition: background-color 0.2s, opacity 0.2s; +} + +.note-modal-actions-left > button:hover, +.note-modal-actions-left > a:hover, +.note-modal-actions-left > .note-modal-color-picker > button:hover { + opacity: 1; + background: rgba(255, 255, 255, 0.14); +} + +.note-modal-color-picker { + position: relative; + display: inline-flex; +} + +.note-modal-palette { + left: -8px; + bottom: calc(100% + 8px); + width: min(500px, 92vw); +} + +.note-modal-close-btn { + background: transparent; + border: none; + color: inherit; + font-weight: 600; + border-radius: 8px; + padding: 6px 10px; + cursor: pointer; +} + +.note-modal-close-btn:hover { + background: rgba(255, 255, 255, 0.14); +} + +@media (max-width: 640px) { + .note-modal { + width: 96vw; + max-height: 92vh; + } + + .note-modal .note-title { + font-size: 1.25rem; + } } \ No newline at end of file diff --git a/shaarli-pro/img/favicon.png b/shaarli-pro/img/favicon.png new file mode 100644 index 0000000..9dab0f8 Binary files /dev/null and b/shaarli-pro/img/favicon.png differ diff --git a/shaarli-pro/img/favicon.svg b/shaarli-pro/img/favicon.svg deleted file mode 100644 index 6ff16e3..0000000 --- a/shaarli-pro/img/favicon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/shaarli-pro/img/gg-note-bg-light/bg-JournalDeTravail.png b/shaarli-pro/img/gg-note-bg-light/bg-JournalDeTravail.png new file mode 100644 index 0000000..f609893 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-JournalDeTravail.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-Radio.png b/shaarli-pro/img/gg-note-bg-light/bg-Radio.png new file mode 100644 index 0000000..3967cc8 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-Radio.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-cafe.png b/shaarli-pro/img/gg-note-bg-light/bg-cafe.png new file mode 100644 index 0000000..f242566 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-cafe.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-cerisier.png b/shaarli-pro/img/gg-note-bg-light/bg-cerisier.png new file mode 100644 index 0000000..752deea Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-cerisier.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-codes.png b/shaarli-pro/img/gg-note-bg-light/bg-codes.png new file mode 100644 index 0000000..aee42a8 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-codes.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-feuilleLigne.png b/shaarli-pro/img/gg-note-bg-light/bg-feuilleLigne.png new file mode 100644 index 0000000..9839d52 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-feuilleLigne.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-feuilleQuadrille.png b/shaarli-pro/img/gg-note-bg-light/bg-feuilleQuadrille.png new file mode 100644 index 0000000..cfe48aa Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-feuilleQuadrille.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-foret.png b/shaarli-pro/img/gg-note-bg-light/bg-foret.png new file mode 100644 index 0000000..b1f2cb9 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-foret.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-legumes.png b/shaarli-pro/img/gg-note-bg-light/bg-legumes.png new file mode 100644 index 0000000..6c13c80 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-legumes.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-livres.png b/shaarli-pro/img/gg-note-bg-light/bg-livres.png new file mode 100644 index 0000000..b19dedc Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-livres.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-montagnes.png b/shaarli-pro/img/gg-note-bg-light/bg-montagnes.png new file mode 100644 index 0000000..391bfbe Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-montagnes.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-ocean.png b/shaarli-pro/img/gg-note-bg-light/bg-ocean.png new file mode 100644 index 0000000..bbd82a7 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-ocean.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-vague1.png b/shaarli-pro/img/gg-note-bg-light/bg-vague1.png new file mode 100644 index 0000000..52b9c21 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-vague1.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-vague2.png b/shaarli-pro/img/gg-note-bg-light/bg-vague2.png new file mode 100644 index 0000000..e298490 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-vague2.png differ diff --git a/shaarli-pro/img/gg-note-bg-light/bg-valise.png b/shaarli-pro/img/gg-note-bg-light/bg-valise.png new file mode 100644 index 0000000..d6fb139 Binary files /dev/null and b/shaarli-pro/img/gg-note-bg-light/bg-valise.png differ diff --git a/shaarli-pro/img/note-backgrounds/README.md b/shaarli-pro/img/note-backgrounds/README.md new file mode 100644 index 0000000..58c1ac1 --- /dev/null +++ b/shaarli-pro/img/note-backgrounds/README.md @@ -0,0 +1,34 @@ +# Note backgrounds (Shaarli Pro) + +Ce dossier contient les images de fond utilisées par la palette des notes (`notebg-*`). + +## Fichiers attendus + +Le thème référence actuellement ces fichiers : + +- `bg-canyon.png` +- `bg-leaves.png` +- `bg-shore.png` +- `bg-sunset.png` +- `bg-planet.png` +- `bg-crystal.png` +- `bg-orchid.png` +- `bg-lake.png` +- `bg-ladder.png` +- `bg-burst.png` + +## Formats recommandés + +- **SVG** (idéal pour motifs/illustrations légères) +- **JPG / JPEG** (photos) +- **PNG** (illustrations avec transparence) +- **WEBP** (poids réduit, recommandé) + +## Conseils d'image + +- Ratio conseillé: **16:9** ou **4:3** +- Taille conseillée: entre **1200x800** et **1920x1080** +- Poids conseillé: **< 500 KB** par image pour garder l'interface fluide +- Privilégier des visuels **peu chargés** pour conserver la lisibilité du texte + +Si vous remplacez une image, conservez le même nom de fichier pour qu'elle apparaisse automatiquement dans la palette. diff --git a/shaarli-pro/img/note-backgrounds/bg-burst.png b/shaarli-pro/img/note-backgrounds/bg-burst.png new file mode 100644 index 0000000..71c144d Binary files /dev/null and b/shaarli-pro/img/note-backgrounds/bg-burst.png differ diff --git a/shaarli-pro/img/note-backgrounds/bg-canyon.png b/shaarli-pro/img/note-backgrounds/bg-canyon.png new file mode 100644 index 0000000..5149df4 Binary files /dev/null and b/shaarli-pro/img/note-backgrounds/bg-canyon.png differ diff --git a/shaarli-pro/img/note-backgrounds/bg-crystal.png b/shaarli-pro/img/note-backgrounds/bg-crystal.png new file mode 100644 index 0000000..ca143c3 Binary files /dev/null and b/shaarli-pro/img/note-backgrounds/bg-crystal.png differ diff --git a/shaarli-pro/img/note-backgrounds/bg-ladder.png b/shaarli-pro/img/note-backgrounds/bg-ladder.png new file mode 100644 index 0000000..b65c490 Binary files /dev/null and b/shaarli-pro/img/note-backgrounds/bg-ladder.png differ diff --git a/shaarli-pro/img/note-backgrounds/bg-lake.png b/shaarli-pro/img/note-backgrounds/bg-lake.png new file mode 100644 index 0000000..b822f2a Binary files /dev/null and b/shaarli-pro/img/note-backgrounds/bg-lake.png differ diff --git a/shaarli-pro/img/note-backgrounds/bg-leaves.png b/shaarli-pro/img/note-backgrounds/bg-leaves.png new file mode 100644 index 0000000..a2e2965 Binary files /dev/null and b/shaarli-pro/img/note-backgrounds/bg-leaves.png differ diff --git a/shaarli-pro/img/note-backgrounds/bg-orchid.png b/shaarli-pro/img/note-backgrounds/bg-orchid.png new file mode 100644 index 0000000..1d2fc98 Binary files /dev/null and b/shaarli-pro/img/note-backgrounds/bg-orchid.png differ diff --git a/shaarli-pro/img/note-backgrounds/bg-planet.png b/shaarli-pro/img/note-backgrounds/bg-planet.png new file mode 100644 index 0000000..6047b81 Binary files /dev/null and b/shaarli-pro/img/note-backgrounds/bg-planet.png differ diff --git a/shaarli-pro/img/note-backgrounds/bg-shore.png b/shaarli-pro/img/note-backgrounds/bg-shore.png new file mode 100644 index 0000000..ca3f510 Binary files /dev/null and b/shaarli-pro/img/note-backgrounds/bg-shore.png differ diff --git a/shaarli-pro/img/note-backgrounds/bg-sunset.png b/shaarli-pro/img/note-backgrounds/bg-sunset.png new file mode 100644 index 0000000..f0224d9 Binary files /dev/null and b/shaarli-pro/img/note-backgrounds/bg-sunset.png differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_JournalDeTravail.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_JournalDeTravail.jpg new file mode 100644 index 0000000..fdee291 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_JournalDeTravail.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_cafe.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_cafe.jpg new file mode 100644 index 0000000..668ef51 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_cafe.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_codes.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_codes.jpg new file mode 100644 index 0000000..3ae8aef Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_codes.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_dune.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_dune.jpg new file mode 100644 index 0000000..05b17e5 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_dune.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_feuilleLigne.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_feuilleLigne.jpg new file mode 100644 index 0000000..b79dc4f Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_feuilleLigne.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_feuilleQuadrille.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_feuilleQuadrille.jpg new file mode 100644 index 0000000..077e8a8 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_feuilleQuadrille.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_fleurs.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_fleurs.jpg new file mode 100644 index 0000000..219ee57 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_fleurs.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_foret.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_foret.jpg new file mode 100644 index 0000000..59126a7 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_foret.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_grid.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_grid.jpg new file mode 100644 index 0000000..aa4822f Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_grid.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_legumes.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_legumes.jpg new file mode 100644 index 0000000..96e3bad Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_legumes.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_livres.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_livres.jpg new file mode 100644 index 0000000..03d4d72 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_livres.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_montagnes.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_montagnes.jpg new file mode 100644 index 0000000..a3813a9 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_montagnes.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_ocean.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_ocean.jpg new file mode 100644 index 0000000..61479e2 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_ocean.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_sports.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_sports.jpg new file mode 100644 index 0000000..296c126 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_sports.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_vague1.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_vague1.jpg new file mode 100644 index 0000000..20c0c38 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_vague1.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_vague2.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_vague2.jpg new file mode 100644 index 0000000..ae61226 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_vague2.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_ville.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_ville.jpg new file mode 100644 index 0000000..2326cf9 Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_ville.jpg differ diff --git a/shaarli-pro/img/note-bg-dark/bg-Dark_voyages.jpg b/shaarli-pro/img/note-bg-dark/bg-Dark_voyages.jpg new file mode 100644 index 0000000..b786cad Binary files /dev/null and b/shaarli-pro/img/note-bg-dark/bg-Dark_voyages.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-cafe.jpg b/shaarli-pro/img/note-bg-light/bg-light-cafe.jpg new file mode 100644 index 0000000..885098d Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-cafe.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-codes.jpg b/shaarli-pro/img/note-bg-light/bg-light-codes.jpg new file mode 100644 index 0000000..ce4b141 Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-codes.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-feuilleLignes.jpg b/shaarli-pro/img/note-bg-light/bg-light-feuilleLignes.jpg new file mode 100644 index 0000000..eaf05ac Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-feuilleLignes.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-feuilleQuadrilles.jpg b/shaarli-pro/img/note-bg-light/bg-light-feuilleQuadrilles.jpg new file mode 100644 index 0000000..f4ef2b8 Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-feuilleQuadrilles.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-fleur.jpg b/shaarli-pro/img/note-bg-light/bg-light-fleur.jpg new file mode 100644 index 0000000..f1668c6 Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-fleur.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-foret.jpg b/shaarli-pro/img/note-bg-light/bg-light-foret.jpg new file mode 100644 index 0000000..ebd110e Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-foret.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-journal.jpg b/shaarli-pro/img/note-bg-light/bg-light-journal.jpg new file mode 100644 index 0000000..3095c49 Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-journal.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-lecture.jpg b/shaarli-pro/img/note-bg-light/bg-light-lecture.jpg new file mode 100644 index 0000000..cebcee4 Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-lecture.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-legumes.jpg b/shaarli-pro/img/note-bg-light/bg-light-legumes.jpg new file mode 100644 index 0000000..18e8ef2 Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-legumes.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-montagnes.jpg b/shaarli-pro/img/note-bg-light/bg-light-montagnes.jpg new file mode 100644 index 0000000..f2bb13c Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-montagnes.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-ocean.jpg b/shaarli-pro/img/note-bg-light/bg-light-ocean.jpg new file mode 100644 index 0000000..3135cab Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-ocean.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-vague1.jpg b/shaarli-pro/img/note-bg-light/bg-light-vague1.jpg new file mode 100644 index 0000000..e0212a6 Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-vague1.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-vague2.jpg b/shaarli-pro/img/note-bg-light/bg-light-vague2.jpg new file mode 100644 index 0000000..56b62c3 Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-vague2.jpg differ diff --git a/shaarli-pro/img/note-bg-light/bg-light-voyage.jpg b/shaarli-pro/img/note-bg-light/bg-light-voyage.jpg new file mode 100644 index 0000000..cfe95bd Binary files /dev/null and b/shaarli-pro/img/note-bg-light/bg-light-voyage.jpg differ diff --git a/shaarli-pro/includes.html b/shaarli-pro/includes.html index e8ecbc1..f990884 100644 --- a/shaarli-pro/includes.html +++ b/shaarli-pro/includes.html @@ -2,7 +2,7 @@ - + @@ -23,11 +23,11 @@ - - + + {if="$pageName=='editlink' || $pageName=='addlink' || $pageName=='editlinkbatch'"} - - + + {/if} @@ -43,7 +43,7 @@ {loop="$plugins_includes.css_files"} - + {/loop} {if="is_file('data/user.css')"} @@ -54,15 +54,15 @@ var shaarli = { basePath: '{$base_path}', rootPath: '{$root_path}', -assetPath: '/{function="ltrim($asset_path, '/')"}', +assetPath: '{$base_path}{$asset_path}', isAuth: {if="$is_logged_in"}true{else}false{/if}, pageName: '{$pageName}', visibility: '{$visibility}', untaggedonly: {if="$untaggedonly"}true{else}false{/if} }; - - + + {if="file_exists('tpl/shaarli-pro/extra.html')"} {include="extra"} diff --git a/shaarli-pro/js/custom_views.js b/shaarli-pro/js/custom_views.js index 8d2d2a3..89cd0bd 100644 --- a/shaarli-pro/js/custom_views.js +++ b/shaarli-pro/js/custom_views.js @@ -6,6 +6,10 @@ document.addEventListener("DOMContentLoaded", function () { const linkList = document.getElementById("links-list"); const container = document.querySelector(".content-container"); + if (typeof initBookmarkPaletteButtons === "function") { + initBookmarkPaletteButtons(); + } + // Always init Pinned Items logic (sorting and listeners) // This function is defined at the end of the file if (typeof initPinnedItems === "function") { @@ -21,6 +25,263 @@ document.addEventListener("DOMContentLoaded", function () { } }); +const NOTE_COLOR_OPTIONS = [ + { key: "default", label: "Par défaut", hex: "#20293A" }, + { key: "red", label: "Rouge", hex: "#f28b82" }, + { key: "orange", label: "Orange", hex: "#fbbc04" }, + { key: "yellow", label: "Jaune", hex: "#fff475" }, + { key: "green", label: "Vert", hex: "#ccff90" }, + { key: "teal", label: "Menthe", hex: "#a7ffeb" }, + { key: "blue", label: "Bleu clair", hex: "#cbf0f8" }, + { key: "darkblue", label: "Bleu", hex: "#aecbfa" }, + { key: "purple", label: "Violet", hex: "#d7aefb" }, + { key: "pink", label: "Rose", hex: "#fdcfe8" }, + { key: "brown", label: "Beige", hex: "#e6c9a8" }, + { key: "grey", label: "Gris", hex: "#e8eaed" }, +]; + +const NOTE_BACKGROUND_TAG_PREFIX = "notebg-"; + +function resolveThemeAssetBasePath() { + const cssLink = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).find( + (link) => link.href && link.href.includes("/custom_views.css"), + ); + if (cssLink && cssLink.href) { + const cssUrl = new URL(cssLink.href, window.location.origin); + const cssPath = cssUrl.pathname.replace(/\/css\/custom_views\.css$/, ""); + if (cssPath) return cssPath; + } + + const jsScript = Array.from(document.querySelectorAll("script[src]")).find( + (script) => script.src && script.src.includes("/custom_views.js"), + ); + if (jsScript && jsScript.src) { + const jsUrl = new URL(jsScript.src, window.location.origin); + const jsPath = jsUrl.pathname.replace(/\/js\/custom_views\.js$/, ""); + if (jsPath) return jsPath; + } + + if (window.shaarli && typeof window.shaarli.assetPath === "string" && window.shaarli.assetPath.trim() !== "") { + return window.shaarli.assetPath.replace(/\/$/, ""); + } + + return "/tpl/shaarli-pro"; +} + +const NOTE_BACKGROUND_BASE_PATH = `${resolveThemeAssetBasePath().replace(/\/$/, "")}/img/note-backgrounds`; +const NOTE_BACKGROUND_OPTIONS = [ + { key: "bg-canyon", label: "Canyon", file: "bg-canyon.png" }, + { key: "bg-leaves", label: "Feuilles", file: "bg-leaves.png" }, + { key: "bg-shore", label: "Rivage", file: "bg-shore.png" }, + { key: "bg-sunset", label: "Coucher", file: "bg-sunset.png" }, + { key: "bg-planet", label: "Planète", file: "bg-planet.png" }, + { key: "bg-crystal", label: "Cristal", file: "bg-crystal.png" }, + { key: "bg-orchid", label: "Orchidée", file: "bg-orchid.png" }, + { key: "bg-lake", label: "Lac", file: "bg-lake.png" }, + { key: "bg-ladder", label: "Échelle", file: "bg-ladder.png" }, + { key: "bg-burst", label: "Étoile", file: "bg-burst.png" }, +]; + +function getNoteBackgroundUrl(backgroundKey) { + const found = NOTE_BACKGROUND_OPTIONS.find((bg) => bg.key === backgroundKey); + if (!found) return ""; + + return `${NOTE_BACKGROUND_BASE_PATH}/${found.file}`; +} + +function positionPalettePopup(popup) { + if (!popup || !popup.classList.contains("open")) return; + + popup.classList.remove("open-down"); + popup.style.left = ""; + popup.style.right = ""; + + const viewportPadding = 8; + const upRect = popup.getBoundingClientRect(); + if (upRect.top < viewportPadding) { + popup.classList.add("open-down"); + + const downRect = popup.getBoundingClientRect(); + if (downRect.bottom > window.innerHeight - viewportPadding) { + popup.classList.remove("open-down"); + } + } + + let rect = popup.getBoundingClientRect(); + if (rect.right > window.innerWidth - viewportPadding) { + popup.style.left = "auto"; + popup.style.right = "0"; + rect = popup.getBoundingClientRect(); + } + + if (rect.left < viewportPadding) { + popup.style.left = "0"; + popup.style.right = "auto"; + } +} + +function applyNoteVisualState(element, note) { + if (!element || !note) return; + + const color = note.color || "default"; + const background = note.background || "none"; + + element.classList.forEach((cls) => { + if (cls.startsWith("note-color-")) element.classList.remove(cls); + }); + element.classList.add(`note-color-${color}`); + + if (background && background !== "none") { + const bgUrl = getNoteBackgroundUrl(background); + if (bgUrl) { + element.classList.add("note-has-bg"); + element.style.setProperty("--note-bg-image", `url('${bgUrl}')`); + element.dataset.background = background; + } else { + element.classList.remove("note-has-bg"); + element.style.removeProperty("--note-bg-image"); + element.dataset.background = "none"; + } + } else { + element.classList.remove("note-has-bg"); + element.style.removeProperty("--note-bg-image"); + element.dataset.background = "none"; + } + + element.dataset.color = color; +} + +function extractNoteVisualStateFromTags(tags) { + const safeTags = Array.isArray(tags) ? tags : []; + + let color = "default"; + const foundColorTag = safeTags.find((t) => typeof t === "string" && t.startsWith("note-")); + if (foundColorTag) { + const candidate = foundColorTag.substring(5); + if (NOTE_COLOR_OPTIONS.some((opt) => opt.key === candidate)) { + color = candidate; + } + } + + let background = "none"; + const foundBgTag = safeTags.find((t) => typeof t === "string" && t.startsWith(NOTE_BACKGROUND_TAG_PREFIX)); + if (foundBgTag) { + const candidate = foundBgTag.substring(NOTE_BACKGROUND_TAG_PREFIX.length); + if (candidate && NOTE_BACKGROUND_OPTIONS.some((bg) => bg.key === candidate)) { + background = candidate; + } + } + + return { color, background }; +} + +function initBookmarkPaletteButtons() { + const linkCards = Array.from(document.querySelectorAll(".link-outer")); + if (linkCards.length === 0) return; + + linkCards.forEach((card) => { + const cardId = card.dataset.id || card.getAttribute("data-id") || card.id; + if (!cardId) return; + + const tags = Array.from(card.querySelectorAll(".link-tag a")).map((a) => (a.textContent || "").trim()); + const { color, background } = extractNoteVisualStateFromTags(tags); + applyNoteVisualState(card, { color, background }); + + const actions = card.querySelector(".link-actions"); + if (!actions) return; + if (actions.querySelector(".bookmark-palette")) return; + + const editLink = actions.querySelector('a[title="Modifier"], a[aria-label="Modifier ce bookmark"], a[href*="/admin/shaare/"]'); + if (!editLink || !editLink.href) return; + const editUrl = editLink.href; + + const paletteBtnId = `bookmark-palette-${cardId}`; + const wrapper = document.createElement("div"); + wrapper.className = "bookmark-palette"; + wrapper.style.position = "relative"; + wrapper.innerHTML = ` + + + `; + + const pinLink = Array.from(actions.querySelectorAll('a[href*="/pin"], a[aria-label*="Épingler"], a[title*="Épingler"]'))[0]; + if (pinLink && pinLink.parentNode === actions) { + actions.insertBefore(wrapper, pinLink); + } else if (editLink && editLink.parentNode === actions) { + actions.insertBefore(wrapper, editLink.nextSibling); + } else { + actions.appendChild(wrapper); + } + + const paletteBtn = wrapper.querySelector(`#${paletteBtnId}`); + const palettePopup = wrapper.querySelector(`#popup-${paletteBtnId}`); + + if (!paletteBtn || !palettePopup) return; + + paletteBtn.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + document.querySelectorAll(".palette-popup.open").forEach((p) => { + if (p !== palettePopup) { + p.classList.remove("open"); + const parentCard = p.closest(".link-outer"); + if (parentCard) parentCard.classList.remove("palette-open"); + } + }); + const nextOpenState = !palettePopup.classList.contains("open"); + palettePopup.classList.toggle("open"); + card.classList.toggle("palette-open", nextOpenState); + positionPalettePopup(palettePopup); + }); + }); + + document.addEventListener("click", (e) => { + if (e.target.closest(".bookmark-palette")) return; + document.querySelectorAll(".palette-popup.open").forEach((p) => p.classList.remove("open")); + document.querySelectorAll(".link-outer.palette-open").forEach((el) => el.classList.remove("palette-open")); + }); +} + +function generateBookmarkPaletteButtons(bookmarkId, editUrl, currentColor, currentBackground) { + const color = currentColor || "default"; + const background = currentBackground || "none"; + + const colorButtons = [ + ``, + ...NOTE_COLOR_OPTIONS.filter((opt) => opt.key !== "default").map( + (opt) => + ``, + ), + ].join(""); + + const backgroundButtons = [ + ``, + ...NOTE_BACKGROUND_OPTIONS.map((bg) => { + const bgUrl = getNoteBackgroundUrl(bg.key); + return ``; + }), + ].join(""); + + return ` +
${colorButtons}
+
${backgroundButtons}
+ `; +} + +function syncNoteFromCardElement(note, card) { + if (!note || !card) return; + + const colorClass = Array.from(card.classList).find((cls) => cls.startsWith("note-color-")); + if (colorClass) { + note.color = colorClass.replace("note-color-", "") || "default"; + } + + const background = card.dataset.background; + note.background = background && background !== "none" ? background : "none"; +} + /** * Initialize the Google Tasks-like view */ @@ -305,10 +566,29 @@ function initNoteView(linkList, container) { const modalOverlay = document.createElement("div"); modalOverlay.className = "note-modal-overlay"; modalOverlay.innerHTML = ` -
+
+
+

+ +
+
- +
+
+ +
+
+ + + + + + +
+
`; @@ -337,6 +617,103 @@ function initNoteView(linkList, container) { modalOverlay.addEventListener("click", (e) => { if (e.target === modalOverlay) modalOverlay.classList.remove("open"); }); + + const modalPinBtn = modalOverlay.querySelector("#note-modal-pin"); + const modalColorBtn = modalOverlay.querySelector("#note-modal-color-btn"); + const modalColorPopup = modalOverlay.querySelector("#note-modal-color-popup"); + + modalColorBtn.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + modalColorPopup.classList.toggle("open"); + positionPalettePopup(modalColorPopup); + }); + + modalPinBtn.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + + const modalCard = modalOverlay.querySelector(".note-modal"); + const noteId = modalCard.dataset.noteId; + const editUrl = modalCard.dataset.editUrl; + + if (!noteId || !editUrl) return; + + togglePinTag(noteId, editUrl, modalPinBtn); + + const isPinned = modalPinBtn.classList.contains("active"); + let tags = (modalCard.dataset.tags || "") + .split("||") + .filter((t) => t); + + if (isPinned) { + if (!tags.includes("shaarli-pin")) tags.push("shaarli-pin"); + } else { + tags = tags.filter((t) => t !== "shaarli-pin"); + } + + modalCard.dataset.tags = tags.join("||"); + renderModalTags(modalOverlay.querySelector("#note-modal-tags"), tags); + + if (modalOverlay.currentNote) { + modalOverlay.currentNote.isPinned = isPinned; + modalOverlay.currentNote.tags = tags; + } + }); + + modalOverlay.querySelector("#note-modal-delete").addEventListener("click", () => { + const modalCard = modalOverlay.querySelector(".note-modal"); + const deleteUrl = modalCard.dataset.deleteUrl; + if (deleteUrl && deleteUrl !== "#") { + window.location.href = deleteUrl; + } + }); + + modalOverlay.querySelector("#note-modal-archive").addEventListener("click", (e) => { + e.preventDefault(); + + const modalCard = modalOverlay.querySelector(".note-modal"); + const noteId = modalCard.dataset.noteId; + const editUrl = modalCard.dataset.editUrl; + if (!noteId || !editUrl) return; + + addTagToNote(editUrl, "shaarli-archiver") + .then(() => { + if (modalOverlay.currentNote) { + if (!modalOverlay.currentNote.tags.includes("shaarli-archiver")) { + modalOverlay.currentNote.tags.push("shaarli-archiver"); + } + } + + const noteCard = document.querySelector(`.note-card[data-id="${noteId}"]`); + if (noteCard) noteCard.remove(); + + const index = notes.findIndex((n) => String(n.id) === String(noteId)); + if (index > -1) notes.splice(index, 1); + + modalOverlay.classList.remove("open"); + }) + .catch((err) => { + console.error("Error archiving note:", err); + alert("Erreur lors de l'archivage de la note."); + }); + }); + + modalOverlay.addEventListener("click", (e) => { + if (!e.target.closest(".note-modal-color-picker")) { + modalColorPopup.classList.remove("open"); + } + }); + + document.addEventListener("click", (e) => { + if (e.target.closest(".note-hover-actions .palette-popup") || e.target.closest('.note-hover-actions [id^="palette-"]')) { + return; + } + + document.querySelectorAll(".note-hover-actions .palette-popup.open").forEach((p) => { + p.classList.remove("open"); + }); + }); } function parseNoteFromLink(linkEl) { @@ -364,12 +741,20 @@ function parseNoteFromLink(linkEl) { const tags = []; let color = "default"; + let background = "none"; linkEl.querySelectorAll(".link-tag-list a").forEach((tag) => { const t = tag.textContent.trim(); // Check for color tag if (t.startsWith("note-")) { const potentialColor = t.substring(5); - color = potentialColor; + const knownColor = NOTE_COLOR_OPTIONS.some((opt) => opt.key === potentialColor); + if (knownColor) { + color = potentialColor; + } else { + tags.push(t); + } + } else if (t.startsWith(NOTE_BACKGROUND_TAG_PREFIX)) { + background = t.substring(NOTE_BACKGROUND_TAG_PREFIX.length) || "none"; } else { tags.push(t); } @@ -385,30 +770,34 @@ function parseNoteFromLink(linkEl) { // User requested "availability of the tag 'shaarli-pin' as the main source" const isPinned = tags.includes("shaarli-pin"); - return { id, title, descHtml, descText, coverImage, url, tags, color, editUrl, deleteUrl, pinUrl, isPinned }; + return { id, title, descHtml, descText, coverImage, url, tags, color, background, editUrl, deleteUrl, pinUrl, isPinned }; } function renderNotes(container, notes, viewMode) { container.innerHTML = ""; container.className = viewMode === "grid" ? "notes-masonry" : "notes-list-view"; + const visibleNotes = notes.filter((note) => !(note.tags || []).includes("shaarli-archiver")); + // Sort: Pinned items first - notes.sort((a, b) => { + visibleNotes.sort((a, b) => { const aPinned = a.tags.includes("shaarli-pin"); const bPinned = b.tags.includes("shaarli-pin"); return bPinned - aPinned; }); - notes.forEach((note) => { + visibleNotes.forEach((note) => { const card = document.createElement("div"); - card.className = `note-card note-color-${note.color}`; + card.className = "note-card"; card.dataset.id = note.id; + applyNoteVisualState(card, note); if (viewMode === "list") card.classList.add("list-mode"); // Main Click to Open Modal card.addEventListener("click", (e) => { // Prevent if clicking buttons if (e.target.closest("button") || e.target.closest("a") || e.target.closest(".note-hover-actions")) return; + syncNoteFromCardElement(note, card); openNoteModal(note); }); @@ -488,18 +877,14 @@ function renderNotes(container, notes, viewMode) { const palettePopup = actions.querySelector(`#popup-${paletteBtnId}`); paletteBtn.addEventListener("click", (e) => { + e.preventDefault(); e.stopPropagation(); // Close others? document.querySelectorAll(".palette-popup.open").forEach((p) => { if (p !== palettePopup) p.classList.remove("open"); }); palettePopup.classList.toggle("open"); - }); - - // Close palette when clicking outside - // (Handled globally or card based? simple: card mouseleave?) - card.addEventListener("mouseleave", () => { - palettePopup.classList.remove("open"); + positionPalettePopup(palettePopup); }); inner.appendChild(actions); @@ -509,60 +894,203 @@ function renderNotes(container, notes, viewMode) { }); } +function setModalPinButtonState(button, isPinned) { + if (!button) return; + + button.classList.toggle("active", isPinned); + button.title = isPinned ? "Désépingler" : "Épingler"; + + const icon = button.querySelector("i"); + if (icon) { + icon.className = `mdi ${isPinned ? "mdi-pin" : "mdi-pin-outline"}`; + } +} + +function renderModalTags(container, tags) { + if (!container) return; + + container.innerHTML = ""; + const visibleTags = (tags || []).filter((tag) => tag && tag !== "note"); + + if (visibleTags.length === 0) { + container.classList.add("is-empty"); + return; + } + + container.classList.remove("is-empty"); + visibleTags.forEach((tag) => { + const tagEl = document.createElement("span"); + tagEl.className = "note-tag"; + tagEl.textContent = tag; + container.appendChild(tagEl); + }); +} + function openNoteModal(note) { const modal = document.querySelector(".note-modal-overlay"); + if (!modal) return; + + const modalCard = modal.querySelector(".note-modal"); + const title = modal.querySelector("#note-modal-title"); const content = modal.querySelector(".note-modal-content"); + const tagsContainer = modal.querySelector("#note-modal-tags"); + const editLink = modal.querySelector("#note-modal-edit"); + const pinButton = modal.querySelector("#note-modal-pin"); + const modalColorPopup = modal.querySelector("#note-modal-color-popup"); - // Build full content - let html = ` -

${note.title}

-
${note.descHtml}
- `; - // Add images if not in desc? (desc usually has it) + modal.currentNote = note; + + modalCard.className = "note-modal"; + applyNoteVisualState(modalCard, note); + modalCard.dataset.noteId = note.id || ""; + modalCard.dataset.editUrl = note.editUrl || ""; + modalCard.dataset.deleteUrl = note.deleteUrl || ""; + modalCard.dataset.background = note.background || "none"; + + const visibleTags = (note.tags || []).filter((tag) => tag && tag !== "note"); + modalCard.dataset.tags = visibleTags.join("||"); + + title.textContent = note.title || "Sans titre"; + content.innerHTML = `
${note.descHtml || ""}
`; + renderModalTags(tagsContainer, visibleTags); + + if (editLink) { + editLink.href = note.editUrl || "#"; + } + + if (modalColorPopup) { + modalColorPopup.innerHTML = generateModalPaletteButtons(note); + modalColorPopup.classList.remove("open"); + } + + setModalPinButtonState(pinButton, !!note.isPinned); - content.innerHTML = html; modal.classList.add("open"); } -function generatePaletteButtons(note) { - const colors = ["default", "red", "orange", "yellow", "green", "teal", "blue", "darkblue", "purple", "pink", "brown", "grey"]; - // Map to hex for the button background - const colorMap = { - default: "#ffffff", - red: "#f28b82", - orange: "#fbbc04", - yellow: "#fff475", - green: "#ccff90", - teal: "#a7ffeb", - blue: "#cbf0f8", - darkblue: "#aecbfa", - purple: "#d7aefb", - pink: "#fdcfe8", - brown: "#e6c9a8", - grey: "#e8eaed", - }; - // Dark mode mapping could be handled via CSS classes on buttons but inline styles are easier for the picker circles - // We will just use class names and let CSS handle the preview color if possible, or set style. +function generateModalPaletteButtons(note) { + const currentColor = note.color || "default"; + const currentBackground = note.background || "none"; - return colors - .map((c) => { - // We use style for the button background roughly. - // Actually, let's use the class on the button itself to pick up the color from CSS variables if defined, - // OR just hardcode the light mode preview for simplicity as the picker is usually on white/dark background. - return ``; - }) - .join(""); + const colorButtons = [ + ``, + ...NOTE_COLOR_OPTIONS.filter((opt) => opt.key !== "default").map( + (opt) => + ``, + ), + ].join(""); + + const backgroundButtons = [ + ``, + ...NOTE_BACKGROUND_OPTIONS.map((bg) => { + const bgUrl = getNoteBackgroundUrl(bg.key); + return ``; + }), + ].join(""); + + return ` +
${colorButtons}
+
${backgroundButtons}
+ `; +} + +window.setModalNoteColor = function (color) { + const modal = document.querySelector(".note-modal-overlay"); + if (!modal || !modal.currentNote) return; + + const currentNote = modal.currentNote; + setNoteColor(currentNote.id, color, currentNote.editUrl); + + currentNote.color = color; + const modalCard = modal.querySelector(".note-modal"); + if (modalCard) { + applyNoteVisualState(modalCard, currentNote); + } + + const modalColorPopup = modal.querySelector("#note-modal-color-popup"); + if (modalColorPopup) { + modalColorPopup.innerHTML = generateModalPaletteButtons(currentNote); + modalColorPopup.classList.add("open"); + positionPalettePopup(modalColorPopup); + } +}; + +window.setModalNoteBackground = function (backgroundKey) { + const modal = document.querySelector(".note-modal-overlay"); + if (!modal || !modal.currentNote) return; + + const currentNote = modal.currentNote; + setNoteBackground(currentNote.id, backgroundKey, currentNote.editUrl); + + currentNote.background = backgroundKey; + const modalCard = modal.querySelector(".note-modal"); + if (modalCard) { + applyNoteVisualState(modalCard, currentNote); + } + + const modalColorPopup = modal.querySelector("#note-modal-color-popup"); + if (modalColorPopup) { + modalColorPopup.innerHTML = generateModalPaletteButtons(currentNote); + modalColorPopup.classList.add("open"); + positionPalettePopup(modalColorPopup); + } +}; + +function generatePaletteButtons(note) { + const currentColor = note.color || "default"; + const currentBackground = note.background || "none"; + + const colorButtons = [ + ``, + ...NOTE_COLOR_OPTIONS.filter((opt) => opt.key !== "default").map( + (opt) => + ``, + ), + ].join(""); + + const backgroundButtons = [ + ``, + ...NOTE_BACKGROUND_OPTIONS.map((bg) => { + const bgUrl = getNoteBackgroundUrl(bg.key); + return ``; + }), + ].join(""); + + return ` +
${colorButtons}
+
${backgroundButtons}
+ `; } window.setNoteColor = function (noteId, color, editUrl) { // 1. Visual Update (Immediate feedback) const card = document.querySelector(`.note-card[data-id="${noteId}"]`); if (card) { - // Remove all color classes - card.classList.forEach((cls) => { - if (cls.startsWith("note-color-")) card.classList.remove(cls); - }); - card.classList.add(`note-color-${color}`); + const background = card.dataset.background || "none"; + applyNoteVisualState(card, { color, background }); + } + + const bookmarkCard = document.querySelector(`.link-outer[data-id="${noteId}"]`); + if (bookmarkCard) { + const background = bookmarkCard.dataset.background || "none"; + applyNoteVisualState(bookmarkCard, { color, background }); + const palettePopup = bookmarkCard.querySelector(".bookmark-palette .palette-popup"); + if (palettePopup) { + palettePopup.innerHTML = generateBookmarkPaletteButtons(noteId, editUrl, color, background); + } + } + + const modal = document.querySelector(".note-modal-overlay"); + if (modal && modal.currentNote && String(modal.currentNote.id) === String(noteId)) { + modal.currentNote.color = color; + const modalCard = modal.querySelector(".note-modal"); + if (modalCard) { + applyNoteVisualState(modalCard, modal.currentNote); + } + const modalColorPopup = modal.querySelector("#note-modal-color-popup"); + if (modalColorPopup) { + modalColorPopup.innerHTML = generateModalPaletteButtons(modal.currentNote); + } } // 2. Persistence via AJAX Form Submission @@ -592,7 +1120,11 @@ window.setNoteColor = function (noteId, color, editUrl) { let tagsArray = currentTags.split(/[\s,]+/).filter((t) => t.trim() !== ""); // Remove existing color tags - tagsArray = tagsArray.filter((t) => !t.startsWith("note-")); + tagsArray = tagsArray.filter((t) => { + if (!t.startsWith("note-")) return true; + const colorKey = t.substring(5); + return !NOTE_COLOR_OPTIONS.some((opt) => opt.key === colorKey); + }); // Add new color tag (unless default) if (color !== "default") { @@ -624,6 +1156,130 @@ window.setNoteColor = function (noteId, color, editUrl) { }); }; +window.setNoteBackground = function (noteId, backgroundKey, editUrl) { + const card = document.querySelector(`.note-card[data-id="${noteId}"]`); + if (card) { + const colorClass = Array.from(card.classList).find((cls) => cls.startsWith("note-color-")); + const color = colorClass ? colorClass.replace("note-color-", "") : card.dataset.color || "default"; + applyNoteVisualState(card, { color, background: backgroundKey }); + } + + const bookmarkCard = document.querySelector(`.link-outer[data-id="${noteId}"]`); + if (bookmarkCard) { + const colorClass = Array.from(bookmarkCard.classList).find((cls) => cls.startsWith("note-color-")); + const color = colorClass ? colorClass.replace("note-color-", "") : bookmarkCard.dataset.color || "default"; + applyNoteVisualState(bookmarkCard, { color, background: backgroundKey }); + const palettePopup = bookmarkCard.querySelector(".bookmark-palette .palette-popup"); + if (palettePopup) { + palettePopup.innerHTML = generateBookmarkPaletteButtons(noteId, editUrl, color, backgroundKey); + } + } + + const modal = document.querySelector(".note-modal-overlay"); + if (modal && modal.currentNote && String(modal.currentNote.id) === String(noteId)) { + modal.currentNote.background = backgroundKey; + const modalCard = modal.querySelector(".note-modal"); + if (modalCard) { + applyNoteVisualState(modalCard, modal.currentNote); + } + const modalColorPopup = modal.querySelector("#note-modal-color-popup"); + if (modalColorPopup) { + modalColorPopup.innerHTML = generateModalPaletteButtons(modal.currentNote); + } + } + + fetch(editUrl) + .then((response) => response.text()) + .then((html) => { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, "text/html"); + const form = doc.querySelector('form[name="linkform"]'); + + if (!form) throw new Error("Could not find edit form"); + + const formData = new URLSearchParams(); + const inputs = form.querySelectorAll("input, textarea"); + + inputs.forEach((input) => { + if (input.type === "checkbox") { + if (input.checked) formData.append(input.name, input.value || "on"); + } else if (input.name) { + formData.append(input.name, input.value); + } + }); + + let currentTags = formData.get("lf_tags") || ""; + let tagsArray = currentTags.split(/[\s,]+/).filter((t) => t.trim() !== ""); + + tagsArray = tagsArray.filter((t) => !t.startsWith(NOTE_BACKGROUND_TAG_PREFIX)); + if (backgroundKey && backgroundKey !== "none") { + tagsArray.push(`${NOTE_BACKGROUND_TAG_PREFIX}${backgroundKey}`); + } + + formData.set("lf_tags", tagsArray.join(" ")); + formData.append("save_edit", "1"); + + return fetch(form.action, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: formData.toString(), + }); + }) + .then((response) => { + if (!response.ok) { + throw new Error("Failed to save background"); + } + }) + .catch((err) => { + console.error("Error saving note background:", err); + alert("Erreur lors de la sauvegarde du fond. Veuillez rafraîchir la page."); + }); +}; + +function addTagToNote(editUrl, tag) { + return fetch(editUrl) + .then((response) => response.text()) + .then((html) => { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, "text/html"); + const form = doc.querySelector('form[name="linkform"]'); + if (!form) throw new Error("Could not find edit form"); + + const formData = new URLSearchParams(); + const inputs = form.querySelectorAll("input, textarea"); + + inputs.forEach((input) => { + if (input.type === "checkbox") { + if (input.checked) formData.append(input.name, input.value || "on"); + } else if (input.name) { + formData.append(input.name, input.value); + } + }); + + let currentTags = formData.get("lf_tags") || ""; + let tagsArray = currentTags.split(/[\s,]+/).filter((t) => t.trim() !== ""); + + if (!tagsArray.includes(tag)) tagsArray.push(tag); + + formData.set("lf_tags", tagsArray.join(" ")); + formData.append("save_edit", "1"); + + return fetch(form.action, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: formData.toString(), + }); + }) + .then((response) => { + if (!response.ok) throw new Error("Failed to update tags"); + return response; + }); +} + /* ========================================================== PINNED ITEMS LOGIC (Tag: shaarli-pin) ========================================================== */