From f99c1cbecbc105837c2299fb0f333fa9dcebf94f Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Sun, 19 Apr 2026 21:46:47 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20am=C3=A9liorer=20styles=20dark=20mode?= =?UTF-8?q?=20avec=20background=20unifi=C3=A9=20#20293a,=20bordures=20note?= =?UTF-8?q?s/cards=20(1px=20solid=20#000=20light,=20#dedfe2=20dark),=20wid?= =?UTF-8?q?th=20input=20600px=E2=86=92800px,=20ajout=20bouton=20pin=20corn?= =?UTF-8?q?er=20absolu=20(top-right,=20hover=20scale=201.06),=20suppressio?= =?UTF-8?q?n=20backgrounds/borders/outlines=20inputs=20(transparent=20+=20?= =?UTF-8?q?!important),=20nettoyage=20code=20dupliqu=C3=A9=20note-input-co?= =?UTF-8?q?llapsed,=20styles=20todo-draft-row=20transparent,=20et=20correc?= =?UTF-8?q?tions=20mineures=20formatage=20CSS=20(espaces,=20indentation)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shaarli-pro/css/custom_views.css | 298 +++++++++++++++--------- shaarli-pro/css/style.css | 157 ++++++++++++- shaarli-pro/editlink.html | 6 +- shaarli-pro/includes.html | 1 + shaarli-pro/js/custom_views.js | 249 +++++++++++++------- shaarli-pro/js/script.js | 89 +++++++- shaarli-pro/js/shaarit-rules.js | 377 +++++++++++++++++++++++++++++++ shaarli-pro/linklist.html | 5 + shaarli-pro/migrate-todos.php | 104 +++++++++ shaarli-pro/page.header.html | 6 +- shaarli-pro/tools.html | 6 + 11 files changed, 1098 insertions(+), 200 deletions(-) create mode 100644 shaarli-pro/js/shaarit-rules.js create mode 100644 shaarli-pro/migrate-todos.php diff --git a/shaarli-pro/css/custom_views.css b/shaarli-pro/css/custom_views.css index 31b9f6d..0c05dc4 100644 --- a/shaarli-pro/css/custom_views.css +++ b/shaarli-pro/css/custom_views.css @@ -19,7 +19,7 @@ body.view-todo .content-container { } [data-theme="dark"] body.view-todo .content-container { - background-color: var(--bg-body); + background-color: #20293a; } body.view-todo #linklist { @@ -105,7 +105,7 @@ body.view-todo #linklist { } [data-theme="dark"] .todo-main { - background-color: #0f172a; + background-color: #20293a; } .todo-main-header { @@ -212,14 +212,16 @@ body.view-todo #linklist { /* --- NOTES VIEW --- */ /* Wrapper */ -body.view-notes .content-container { +body.view-notes .content-container, +body.view-archive .content-container { padding: 2rem; background-color: var(--bg-body); min-height: 100vh; } -[data-theme="dark"] body.view-notes .content-container { - background-color: var(--bg-body); +[data-theme="dark"] body.view-notes .content-container, +[data-theme="dark"] body.view-archive .content-container { + background-color: #20293a; } /* Tool Bar / Input Area */ @@ -233,7 +235,7 @@ body.view-notes .content-container { } .notes-top-bar-inner { - width: 600px; + width: 800px; max-width: 100%; display: flex; align-items: flex-start; @@ -241,10 +243,11 @@ body.view-notes .content-container { } .note-input-container { - width: 600px; + width: 800px; max-width: 100%; background-color: var(--background-secondary, #ffffff); border-radius: 8px; + border: 1px solid #000; box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3), 0 2px 6px 2px rgba(60, 64, 67, 0.15); transition: box-shadow 0.2s; overflow: hidden; @@ -255,9 +258,9 @@ body.view-notes .content-container { } [data-theme="dark"] .note-input-container { - background-color: #202124; - border: none; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.6), 0 2px 6px 2px rgba(0, 0, 0, 0.4); + background-color: #20293a; + border: 1px solid var(--border); + box-shadow: none; } .note-input-collapsed { @@ -268,6 +271,16 @@ body.view-notes .content-container { color: var(--note-card-fg, var(--text-light, #80868b)); font-weight: 500; font-size: 1rem; + padding: 12px 16px; + cursor: text; + color: var(--note-card-fg, var(--text-light, #80868b)); + font-weight: 500; + font-size: 1rem; + padding: 12px 16px; + cursor: text; + color: var(--note-card-fg, var(--text-light, #80868b)); + font-weight: 500; + font-size: 1rem; } .note-input-container.is-editing { @@ -327,6 +340,14 @@ body.view-notes .content-container { .todo-draft-row .todo-item-text { padding: 5px 4px; font-size: 0.95rem; + background-color: transparent; + border: none !important; + outline: none !important; + box-shadow: none !important; +} + +[data-theme="dark"] .todo-draft-row .todo-item-text { + background-color: transparent; } .todo-draft-add-btn { @@ -355,26 +376,32 @@ body.view-notes .content-container { .note-input-title { width: 100%; - border: 0; - outline: none; - background: transparent; + border: none !important; + outline: none !important; + box-shadow: none !important; + background-color: transparent; color: inherit; font-size: 1.05rem; font-weight: 500; padding: 0; } +[data-theme="dark"] .note-input-title { + background-color: transparent; +} + .note-input-description-source { width: 100%; - border: none; - background: transparent; + border: none !important; + outline: none !important; + box-shadow: none !important; + background-color: transparent; color: inherit; padding: 0; resize: none; min-height: 120px; max-height: 44vh; overflow: auto; - outline: none; display: block; font-size: 0.95rem; line-height: 1.5; @@ -385,7 +412,8 @@ body.view-notes .content-container { } [data-theme="dark"] .note-input-description-source { - border: none; + border: none !important; + background-color: transparent; } .note-input-container.is-enhanced .note-input-description-source { @@ -419,8 +447,15 @@ body.view-notes .content-container { } @keyframes fadeIn { - from { opacity: 0; transform: translateY(-4px); } - to { opacity: 1; transform: translateY(0); } + from { + opacity: 0; + transform: translateY(-4px); + } + + to { + opacity: 1; + transform: translateY(0); + } } .note-format-btn { @@ -476,7 +511,7 @@ body.view-notes .content-container { flex-wrap: wrap; } -.note-input-actions-left > button { +.note-input-actions-left>button { background: transparent; border: none; color: inherit; @@ -491,17 +526,17 @@ body.view-notes .content-container { padding: 0; } -.note-input-actions-left > button:disabled { +.note-input-actions-left>button:disabled { cursor: default; opacity: 0.35; } -.note-input-actions-left > button:hover:not(:disabled) { +.note-input-actions-left>button:hover:not(:disabled) { opacity: 1; background: rgba(0, 0, 0, 0.08); } -[data-theme="dark"] .note-input-actions-left > button:hover:not(:disabled) { +[data-theme="dark"] .note-input-actions-left>button:hover:not(:disabled) { background: rgba(255, 255, 255, 0.14); } @@ -619,15 +654,63 @@ body.view-notes .content-container { column-span: none; border-radius: 12px; overflow: hidden; + border: 1px solid #000; } .note-card { border-radius: 12px; overflow: hidden; + border: 1px solid #000; +} + +[data-theme="dark"] .note-card { + border-color: #dedfe2; } .note-card .note-inner { + position: relative; padding: 14px 16px 12px; + padding-right: 58px; +} + +.note-pin-corner { + position: absolute; + top: 10px; + right: 10px; + width: auto; + height: auto; + display: inline-flex; + align-items: center; + justify-content: center; + text-decoration: none; + color: var(--note-card-fg, currentColor); + background: transparent; + border: none; + z-index: 4; + opacity: 0.92; + transition: transform 0.15s ease, opacity 0.15s ease, color 0.15s ease; +} + +.note-pin-corner i { + font-size: 1.35rem; +} + +.note-pin-corner:hover { + transform: scale(1.06); + opacity: 1; +} + +.note-pin-corner.active { + color: var(--note-card-fg, currentColor); +} + +[data-theme="dark"] .note-pin-corner { + background: transparent; + border: none; +} + +[data-theme="dark"] .note-pin-corner.active { + color: var(--note-card-fg, currentColor); } @media (max-width: 1200px) { @@ -683,7 +766,7 @@ body.view-notes .content-container { overflow: hidden; border-radius: 8px; background: var(--background-secondary, #ffffff); - border: 1px solid rgba(0, 0, 0, 0.12); + border: 1px solid #000; box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22); color: inherit; display: flex; @@ -696,8 +779,8 @@ body.note-modal-open { [data-theme="dark"] .note-modal { background: #202124; - border-color: transparent; - box-shadow: 0 14px 28px rgba(0,0,0,0.5), 0 10px 10px rgba(0,0,0,0.4); + border-color: #dedfe2; + box-shadow: 0 14px 28px rgba(0, 0, 0, 0.5), 0 10px 10px rgba(0, 0, 0, 0.4); } .note-modal-header { @@ -859,9 +942,9 @@ body.note-modal-open { flex-wrap: wrap; } -.note-modal-actions-left > button, -.note-modal-actions-left > a, -.note-modal-actions-left > .note-modal-color-picker > button { +.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; @@ -876,16 +959,16 @@ body.note-modal-open { text-decoration: none; } -.note-modal-actions-left > button:hover, -.note-modal-actions-left > a:hover, -.note-modal-actions-left > .note-modal-color-picker > button:hover { +.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(0, 0, 0, 0.08); } -[data-theme="dark"] .note-modal-actions-left > button:hover, -[data-theme="dark"] .note-modal-actions-left > a:hover, -[data-theme="dark"] .note-modal-actions-left > .note-modal-color-picker > button:hover { +[data-theme="dark"] .note-modal-actions-left>button:hover, +[data-theme="dark"] .note-modal-actions-left>a:hover, +[data-theme="dark"] .note-modal-actions-left>.note-modal-color-picker>button:hover { background: rgba(255, 255, 255, 0.14); } @@ -1163,9 +1246,9 @@ body.note-modal-open { } } -.note-hover-actions > button, -.note-hover-actions > a, -.note-hover-actions > div > button { +.note-hover-actions>button, +.note-hover-actions>a, +.note-hover-actions>div>button { background: transparent; border: none; width: 32px; @@ -1182,17 +1265,17 @@ body.note-modal-open { opacity: 0.7; } -.note-hover-actions > button:hover, -.note-hover-actions > a:hover, -.note-hover-actions > div > button:hover { +.note-hover-actions>button:hover, +.note-hover-actions>a:hover, +.note-hover-actions>div>button:hover { background-color: rgba(0, 0, 0, 0.08); color: inherit; opacity: 1; } -[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 { +[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 { background-color: rgba(255, 255, 255, 0.14); color: inherit; } @@ -1204,7 +1287,7 @@ body.note-modal-open { color: var(--text-muted); } -.bookmark-palette > button { +.bookmark-palette>button { background: none; border: none; width: 36px; @@ -1219,7 +1302,7 @@ body.note-modal-open { transition: background-color 0.2s, color 0.2s; } -.bookmark-palette > button:hover { +.bookmark-palette>button:hover { background: var(--primary-light); color: var(--primary); } @@ -1316,7 +1399,7 @@ body.note-modal-open { justify-content: start; } -.palette-row + .palette-row { +.palette-row+.palette-row { margin-top: 10px; } @@ -1499,7 +1582,7 @@ body.note-modal-open { } .bg-studio-thumb-solid { - background: linear-gradient(135deg, rgba(255,255,255,0.15), rgba(255,255,255,0.02)); + background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.02)); } .bg-studio-tile { @@ -1740,14 +1823,14 @@ body.note-modal-open { /* Reference: Keep Colors */ /* Default */ .note-card.note-color-default { - background-color: #20293a; - border-color: transparent; - --note-card-fg: #dbe7ff; + background-color: var(--bg-body); + border-color: #000; + --note-card-fg: var(--text-main, #111827); } [data-theme="dark"] .note-card.note-color-default { background-color: #20293a; - border-color: transparent; + border-color: #dedfe2; --note-card-fg: #dbe7ff; } @@ -1883,25 +1966,25 @@ body.note-modal-open { /* Grey */ .note-card.note-color-grey { -background-color: #e8eaed; -border-color: transparent; ---note-card-fg: #2a2d31; + background-color: #e8eaed; + border-color: transparent; + --note-card-fg: #2a2d31; } .note-card.note-has-bg, .note-modal.note-has-bg, .note-input-container.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 bottom; + 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 bottom; } .note-card.todo-card.note-has-bg[data-font-color="auto"] { ---note-card-fg: rgba(255, 255, 255, 0.92); -color: var(--note-card-fg); -background-image: linear-gradient(rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.42)), var(--note-bg-image); -text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); + --note-card-fg: rgba(255, 255, 255, 0.92); + color: var(--note-card-fg); + background-image: linear-gradient(rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.42)), var(--note-bg-image); + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); background-image: linear-gradient(rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.42)), var(--note-bg-image); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); } @@ -1916,6 +1999,11 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); .note-card[class*="note-color-"] { color: var(--note-card-fg, #202124); + border: 1px solid #000; +} + +[data-theme="dark"] .note-card[class*="note-color-"] { + border: 1px solid #dedfe2; } .link-outer { @@ -1994,13 +2082,14 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); /* Responsive: sur petits écrans, ajuster les backgrounds */ @media (max-width: 768px) { + /* En mobile, les cards sont plus étroites, on ajuste */ .notes-masonry .note-card.note-has-bg, .view-grid .link-outer.note-has-bg { background-size: cover; background-position: center bottom; } - + /* Vue liste en mobile: thumbnail passe en haut */ .view-list .link-outer.note-has-bg { background-size: cover; @@ -2052,21 +2141,17 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); .note-card.note-filter-lined, .note-modal.note-filter-lined, .link-outer.note-filter-lined { - background-image: repeating-linear-gradient( - transparent, - transparent 29px, - rgba(0, 0, 0, 0.1) 30px - ) !important; + background-image: repeating-linear-gradient(transparent, + transparent 29px, + rgba(0, 0, 0, 0.1) 30px) !important; } [data-theme="dark"] .note-card.note-filter-lined, [data-theme="dark"] .note-modal.note-filter-lined, [data-theme="dark"] .link-outer.note-filter-lined { - background-image: repeating-linear-gradient( - transparent, - transparent 29px, - rgba(255, 255, 255, 0.1) 30px - ) !important; + background-image: repeating-linear-gradient(transparent, + transparent 29px, + rgba(255, 255, 255, 0.1) 30px) !important; } /* 4. Quadrillé - Grid */ @@ -2126,25 +2211,21 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); .note-card.note-filter-stripes, .note-modal.note-filter-stripes, .link-outer.note-filter-stripes { - background-image: repeating-linear-gradient( - 45deg, - transparent, - transparent 10px, - rgba(0, 0, 0, 0.05) 10px, - rgba(0, 0, 0, 0.05) 20px - ) !important; + background-image: repeating-linear-gradient(45deg, + transparent, + transparent 10px, + rgba(0, 0, 0, 0.05) 10px, + rgba(0, 0, 0, 0.05) 20px) !important; } [data-theme="dark"] .note-card.note-filter-stripes, [data-theme="dark"] .note-modal.note-filter-stripes, [data-theme="dark"] .link-outer.note-filter-stripes { - background-image: repeating-linear-gradient( - 45deg, - transparent, - transparent 10px, - rgba(255, 255, 255, 0.05) 10px, - rgba(255, 255, 255, 0.05) 20px - ) !important; + background-image: repeating-linear-gradient(45deg, + transparent, + transparent 10px, + rgba(255, 255, 255, 0.05) 10px, + rgba(255, 255, 255, 0.05) 20px) !important; } /* ========================================= @@ -2284,7 +2365,7 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); height: 16px; border: 2px solid white; border-radius: 50%; - box-shadow: 0 2px 6px rgba(0,0,0,0.4), inset 0 0 0 1px rgba(0,0,0,0.2); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4), inset 0 0 0 1px rgba(0, 0, 0, 0.2); transform: translate(-50%, -50%); pointer-events: none; } @@ -2312,7 +2393,7 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); border-radius: 50%; background: white; border: 2px solid rgba(15, 23, 42, 0.3); - box-shadow: 0 2px 6px rgba(0,0,0,0.3); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); cursor: pointer; } @@ -2322,7 +2403,7 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); border-radius: 50%; background: white; border: 2px solid rgba(15, 23, 42, 0.3); - box-shadow: 0 2px 6px rgba(0,0,0,0.3); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); cursor: pointer; } @@ -2478,12 +2559,12 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); .bg-studio-swatch:hover { transform: scale(1.12); - box-shadow: 0 6px 16px rgba(0,0,0,0.3); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3); } .bg-studio-swatch.is-active { border: 2px solid rgba(255, 255, 255, 0.95); - box-shadow: 0 4px 12px rgba(0,0,0,0.3); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); } .bg-studio-swatch-custom { @@ -2521,7 +2602,12 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); /* Custom note color style */ .note-card.note-color-custom, .note-modal.note-color-custom { - border-color: transparent; + border-color: #000; +} + +[data-theme="dark"] .note-card.note-color-custom, +[data-theme="dark"] .note-modal.note-color-custom { + border-color: #dedfe2; } /* Ensure Font section is properly sized */ @@ -2533,14 +2619,13 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); } /* --- ARCHIVE VIEW --- */ +/* Ensure archive view uses same background as notes view */ body.view-archive .content-container { - padding: 2rem; - background-color: var(--bg-body); - min-height: 100vh; + background-color: var(--bg-body) !important; } [data-theme="dark"] body.view-archive .content-container { - background-color: var(--bg-body); + background-color: #20293a !important; } /* Archive Title */ @@ -2579,25 +2664,20 @@ body.view-archive .content-container { color: #9aa0a6; } -/* Archive wrapper */ -.archive-wrapper { - max-width: 1200px; - margin: 0 auto; -} - /* Archive top bar adjustments */ .archive-top-bar { flex-direction: column; align-items: center; padding-right: 0; gap: 1rem; + position: relative; } .archive-top-bar .notes-tools { - position: relative; - right: auto; - top: auto; - transform: none; + position: absolute; + right: 1rem; + top: 50%; + transform: translateY(-50%); } body.view-todo .note-card.todo-card .note-body { @@ -2747,12 +2827,12 @@ body.view-todo .note-card.todo-card .todo-checklist-preview-wrap { opacity: 0.75; } -.todo-item-checkbox:checked + .todo-checklist-box-ui { +.todo-item-checkbox:checked+.todo-checklist-box-ui { background: currentColor; box-shadow: inset 0 0 0 3px rgba(0, 0, 0, 0.22); } -[data-theme="dark"] .todo-item-checkbox:checked + .todo-checklist-box-ui { +[data-theme="dark"] .todo-item-checkbox:checked+.todo-checklist-box-ui { box-shadow: inset 0 0 0 3px rgba(0, 0, 0, 0.35); } diff --git a/shaarli-pro/css/style.css b/shaarli-pro/css/style.css index 93743fa..d03db30 100644 --- a/shaarli-pro/css/style.css +++ b/shaarli-pro/css/style.css @@ -325,6 +325,7 @@ a:focus:not(:focus-visible) { .sidebar-add-segment span { display: none; } + .sidebar-add-segment { padding: 0.75rem; } @@ -3246,6 +3247,7 @@ select:focus { .page-edit .toastui-editor-mode-switch { background: var(--bookmark-input-bg); } + .page-edit .toastui-editor-defaultUI { border: 0; } @@ -3266,6 +3268,10 @@ select:focus { background-color: rgba(126, 168, 255, 0.16); } +.page-edit .toastui-editor-toolbar-margin { + margin-bottom: 0.5rem; +} + .page-edit .toastui-editor-toolbar-icons { opacity: 0.96; } @@ -3283,6 +3289,148 @@ select:focus { border-top: 1px solid var(--bookmark-input-border); } +[data-theme="dark"] .page-add .toastui-editor-defaultUI, +[data-theme="dark"] .page-add .toastui-editor-md-container, +[data-theme="dark"] .page-add .toastui-editor-ww-container, +[data-theme="dark"] .page-edit .toastui-editor-defaultUI, +[data-theme="dark"] .page-edit .toastui-editor-md-container, +[data-theme="dark"] .page-edit .toastui-editor-ww-container { + background: var(--toastui-panel-surface, #0a1429) !important; + color: var(--bookmark-text-main, var(--text-main)) !important; + border-color: var(--toastui-border, #1f3560) !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar, +[data-theme="dark"] .page-add .toastui-editor-mode-switch, +[data-theme="dark"] .page-edit .toastui-editor-toolbar, +[data-theme="dark"] .page-edit .toastui-editor-mode-switch { + background: var(--toastui-toolbar-bg, linear-gradient(180deg, rgba(28, 49, 86, 0.95) 0%, rgba(14, 26, 47, 0.96) 100%)) !important; + border-color: var(--toastui-border, #1f3560) !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar, +[data-theme="dark"] .page-edit .toastui-editor-toolbar { + border-bottom: 1px solid var(--toastui-border, #1f3560) !important; + box-shadow: inset 0 -1px 0 rgba(9, 12, 23, 0.6) !important; +} + +[data-theme="dark"] .page-add .toastui-editor-md-tab-container, +[data-theme="dark"] .page-add .toastui-editor-mode-switch, +[data-theme="dark"] .page-edit .toastui-editor-md-tab-container, +[data-theme="dark"] .page-edit .toastui-editor-mode-switch { + border-top: 1px solid var(--toastui-border, #1f3560) !important; +} + +[data-theme="dark"] .page-add .toastui-editor-mode-switch .tab-item, +[data-theme="dark"] .page-edit .toastui-editor-mode-switch .tab-item { + background: transparent !important; + color: var(--bookmark-text-muted, var(--text-secondary)) !important; + border-radius: 6px; + padding: 0.45rem 0.8rem; + transition: background-color 0.2s ease, color 0.2s ease; +} + +[data-theme="dark"] .page-add .toastui-editor-mode-switch .tab-item.active, +[data-theme="dark"] .page-add .toastui-editor-mode-switch .tab-item.selected, +[data-theme="dark"] .page-edit .toastui-editor-mode-switch .tab-item.active, +[data-theme="dark"] .page-edit .toastui-editor-mode-switch .tab-item.selected { + background: var(--toastui-tab-hover, rgba(112, 160, 255, 0.24)) !important; + color: var(--bookmark-text-main, var(--text-main)) !important; + box-shadow: 0 4px 18px rgba(23, 40, 72, 0.55); +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar button, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button { + color: #e2e9ff !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar button:hover, +[data-theme="dark"] .page-add .toastui-editor-toolbar button.active, +[data-theme="dark"] .page-add .toastui-editor-toolbar button:focus-visible, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button:hover, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button.active, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button:focus-visible { + background-color: rgba(118, 164, 255, 0.26) !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar button:hover .toastui-editor-toolbar-icons, +[data-theme="dark"] .page-add .toastui-editor-toolbar button.active .toastui-editor-toolbar-icons, +[data-theme="dark"] .page-add .toastui-editor-toolbar button:focus-visible .toastui-editor-toolbar-icons, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button:hover .toastui-editor-toolbar-icons, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button.active .toastui-editor-toolbar-icons, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button:focus-visible .toastui-editor-toolbar-icons { + opacity: 1 !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar-icons, +[data-theme="dark"] .page-edit .toastui-editor-toolbar-icons { + opacity: 0.85 !important; + filter: none !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar-icons::before, +[data-theme="dark"] .page-add .toastui-editor-toolbar-icons::after, +[data-theme="dark"] .page-edit .toastui-editor-toolbar-icons::before, +[data-theme="dark"] .page-edit .toastui-editor-toolbar-icons::after { + filter: none !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar button.toastui-editor-toolbar-icons, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button.toastui-editor-toolbar-icons { + filter: none !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar button svg, +[data-theme="dark"] .page-add .toastui-editor-toolbar button svg *, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button svg, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button svg * { + fill: rgba(255, 255, 255, 0.85) !important; + stroke: rgba(255, 255, 255, 0.85) !important; + opacity: 1 !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar-divider, +[data-theme="dark"] .page-edit .toastui-editor-toolbar-divider { + background-color: rgba(255, 255, 255, 0.12) !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar *, +[data-theme="dark"] .page-edit .toastui-editor-toolbar * { + background-color: transparent; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar-group, +[data-theme="dark"] .page-edit .toastui-editor-toolbar-group { + background: transparent !important; + border-color: rgba(255, 255, 255, 0.12) !important; + box-shadow: none !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar-group:not(:last-child), +[data-theme="dark"] .page-edit .toastui-editor-toolbar-group:not(:last-child) { + border-right-color: rgba(255, 255, 255, 0.12) !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar-item, +[data-theme="dark"] .page-edit .toastui-editor-toolbar-item { + background-color: transparent !important; + border-color: transparent !important; + box-shadow: none !important; +} + +[data-theme="dark"] .page-add .toastui-editor-toolbar button, +[data-theme="dark"] .page-edit .toastui-editor-toolbar button { + background-color: transparent !important; + border-color: transparent !important; +} + +[data-theme="dark"] .page-add .toastui-editor-contents, +[data-theme="dark"] .page-add .toastui-editor-contents *, +[data-theme="dark"] .page-edit .toastui-editor-contents, +[data-theme="dark"] .page-edit .toastui-editor-contents * { + color: var(--bookmark-text-main, var(--text-main)) !important; +} + .page-edit .bookmark-tags-input { min-height: 48px; display: flex; @@ -3534,6 +3682,11 @@ select:focus { --bookmark-tag-remove-bg: rgba(255, 255, 255, 0.16); --bookmark-tag-remove-bg-hover: rgba(255, 255, 255, 0.26); --bookmark-save-bg: linear-gradient(135deg, #6fa8ff 0%, #4d82f0 100%); + --toastui-panel-bg: #101d38; + --toastui-panel-surface: #0a1429; + --toastui-toolbar-bg: linear-gradient(180deg, rgba(28, 49, 86, 0.95) 0%, rgba(14, 26, 47, 0.96) 100%); + --toastui-border: #1f3560; + --toastui-tab-hover: rgba(112, 160, 255, 0.24); } .page-edit .bookmark-editor-card { @@ -4018,7 +4171,7 @@ select:focus { margin-right: 0; } - .row > [class*="col-"] { + .row>[class*="col-"] { width: 100%; max-width: 100%; float: none; @@ -4577,4 +4730,4 @@ table { .search-results { max-height: calc(90vh - 200px); } -} +} \ No newline at end of file diff --git a/shaarli-pro/editlink.html b/shaarli-pro/editlink.html index 68bc8fe..04fb19d 100644 --- a/shaarli-pro/editlink.html +++ b/shaarli-pro/editlink.html @@ -17,10 +17,10 @@ {/if} {$batchModeValue=empty($batch_mode) ? '0' : '1'} {function="($readLaterChecked = strpos(' ' . $link.tags . ' ', ' readitlater ') != false || strpos(' ' . $link.tags . ' ', ' readlater ') != false || strpos(' ' . $link.tags . ' ', ' toread ') != false) ? '' : ''"} -{function="($isNoteOrTodo = strpos(' ' . $link.tags . ' ', ' note ') != false || strpos(' ' . $link.tags . ' ', ' shaarli-todo ') != false || strpos(' ' . $link.tags . ' ', ' shaarli-todo ') != false) ? '' : ''"} +{function="($isNoteOrTodo = strpos(' ' . $link.tags . ' ', ' note ') != false || strpos(' ' . $link.tags . ' ', ' shaarli-note ') != false || strpos(' ' . $link.tags . ' ', ' todo ') != false || strpos(' ' . $link.tags . ' ', ' shaarli-todo ') != false) ? '' : ''"} {function="($noteDefaultChecked = $link_is_new && empty($link.url) && !$isNoteOrTodo) ? '' : ''"} -{function="($noteChecked = strpos(' ' . $link.tags . ' ', ' note ') != false || $noteDefaultChecked) ? '' : ''"} -{function="($todoChecked = strpos(' ' . $link.tags . ' ', ' shaarli-todo ') != false) ? '' : ''"} +{function="($noteChecked = strpos(' ' . $link.tags . ' ', ' note ') != false || strpos(' ' . $link.tags . ' ', ' shaarli-note ') != false || $noteDefaultChecked) ? '' : ''"} +{function="($todoChecked = strpos(' ' . $link.tags . ' ', ' todo ') != false || strpos(' ' . $link.tags . ' ', ' shaarli-todo ') != false) ? '' : ''"} {$effectiveTags=$link.tags} {if="$noteDefaultChecked && strpos(' ' . $effectiveTags . ' ', ' note ') == false"} {$effectiveTags=trim($effectiveTags . ' note')} diff --git a/shaarli-pro/includes.html b/shaarli-pro/includes.html index 3d6e235..25cbf98 100644 --- a/shaarli-pro/includes.html +++ b/shaarli-pro/includes.html @@ -67,6 +67,7 @@ visibility: '{$visibility}', untaggedonly: (function(){/*{if="$untaggedonly"}*/return true;/*{else}*/return false;/*{/if}*/})() }; + diff --git a/shaarli-pro/js/custom_views.js b/shaarli-pro/js/custom_views.js index 9f0b0eb..175a9a6 100644 --- a/shaarli-pro/js/custom_views.js +++ b/shaarli-pro/js/custom_views.js @@ -2,7 +2,7 @@ document.addEventListener("DOMContentLoaded", function () { // Check URL parameters for custom views const urlParams = new URLSearchParams(window.location.search); const searchTagsRaw = urlParams.get("searchtags") || urlParams.get("searchTags") || ""; - + // Also check URL path for tag format (e.g., /tag/note) const urlPath = window.location.pathname; const pathMatch = urlPath.match(/\/tag\/(.+)$/); @@ -10,17 +10,21 @@ document.addEventListener("DOMContentLoaded", function () { // Parse all active tags to safely detect the view const activeTags = (searchTagsRaw + " " + pathTagRaw).toLowerCase().split(/[\s,]+/).filter(t => t); - + // Foolproof detection using sidebar active state and DOM rendered tags const hasNoteActiveMenu = !!document.querySelector('.sidebar-link[aria-label="Notes"].active, .header-nav-link[aria-label="Notes"].active, .sidebar-link[href*="searchtags=note"].active, .sidebar-link[href*="searchtags=shaarli-note"].active'); - const hasTodoActiveMenu = !!document.querySelector('.sidebar-link[aria-label="Mes tâches"].active, .header-nav-link[aria-label="Mes tâches"].active, .sidebar-link[href*="searchtags=shaarli-todo"].active'); - const hasArchiveActiveMenu = !!document.querySelector('.sidebar-link[aria-label="Archive"].active, .header-nav-link[aria-label="Archive"].active, .sidebar-link[href*="searchtags=shaarli-archive"].active'); - + const hasTodoActiveMenu = !!document.querySelector('.sidebar-link[aria-label="Mes tâches"].active, .header-nav-link[aria-label="Mes tâches"].active, .sidebar-link[href*="searchtags=shaarli-todo"].active, .sidebar-link[href*="searchtags=todo"].active'); + const hasArchiveActiveMenu = !!document.querySelector('.sidebar-link[aria-label="Archive"].active, .header-nav-link[aria-label="Archive"].active, .sidebar-link[href*="searchtags=shaarli-archive"].active, .sidebar-link[href*="searchtags=shaarli-archiver"].active'); + const domChipTags = Array.from(document.querySelectorAll('.search-tag-chip')).map(el => (el.textContent || "").trim().toLowerCase()); - const isNoteView = activeTags.includes("note") || activeTags.includes("shaarli-note") || hasNoteActiveMenu || domChipTags.includes("note") || domChipTags.includes("shaarli-note"); - const isTodoView = activeTags.includes("shaarli-todo") || hasTodoActiveMenu || domChipTags.includes("shaarli-todo"); - const isArchiveView = activeTags.includes("shaarli-archive") || hasArchiveActiveMenu || domChipTags.includes("shaarli-archive"); + const isArchiveTag = (tag) => tag === "shaarli-archive" || tag === "shaarli-archiver"; + const isNoteTag = (tag) => tag === "note" || tag === "shaarli-note" || tag === "#note"; + const isTodoTag = (tag) => tag === "todo" || tag === "shaarli-todo" || tag === "#todo"; + + const isNoteView = activeTags.some(isNoteTag) || hasNoteActiveMenu || domChipTags.some(isNoteTag); + const isTodoView = activeTags.some(isTodoTag) || hasTodoActiveMenu || domChipTags.some(isTodoTag); + const isArchiveView = activeTags.some(isArchiveTag) || hasArchiveActiveMenu || domChipTags.some(isArchiveTag); const linkList = document.getElementById("links-list"); const container = document.querySelector(".content-container"); @@ -194,9 +198,9 @@ document.addEventListener("DOMContentLoaded", function () { const checked = String(todoMatch[1]).toLowerCase() === "x"; html.push( `
  • ` - + `` - + `${renderInline(todoMatch[2])}` - + "
  • ", + + `` + + `${renderInline(todoMatch[2])}` + + "", ); return; } @@ -409,9 +413,9 @@ document.addEventListener("DOMContentLoaded", function () { initBookmarkPaletteButtons(); } - if (typeof organizePinnedBookmarks === "function") { - window.requestAnimationFrame(organizePinnedBookmarks); - } + if (typeof organizePinnedBookmarks === "function") { + window.requestAnimationFrame(organizePinnedBookmarks); + } // Always init Pinned Items logic (sorting and listeners) // This function is defined at the end of the file @@ -466,7 +470,7 @@ document.addEventListener("DOMContentLoaded", function () { // Charger les backgrounds dynamiquement, puis initialiser les vues initThemeModeBackgroundSync(); - + if (typeof loadBackgroundOptions === "function") { loadBackgroundOptions() .then(() => { @@ -500,7 +504,7 @@ const NOTE_COLOR_OPTIONS = [ { key: "default", label: "Par défaut", - light: "#ffffff", + light: "#f8fafc", dark: "#20293A" }, { @@ -605,13 +609,15 @@ const NOTE_BACKGROUND_TAG_PREFIX = "notebg-"; function isTechnicalTag(tag) { if (typeof tag !== "string") return false; - const t = tag.trim(); + const t = tag.trim().toLowerCase(); if (!t) return false; - if (t === "note") return true; + if (t === "note" || t === "#note") return true; if (t === "shaarli-note") return true; + if (t === "todo" || t === "#todo") return true; if (t === "shaarli-todo") return true; if (t === "shaarli-pin") return true; + if (t === "brain-dump") return true; if (t.startsWith(NOTE_FONT_COLOR_TAG_PREFIX)) return true; if (t.startsWith(NOTE_COLOR_TAG_PREFIX)) return true; if (t.startsWith(NOTE_FILTER_TAG_PREFIX)) return true; @@ -694,6 +700,16 @@ function removeTagFromEntity(editUrl, tag) { }); } +function deleteEntitySilently(deleteUrl) { + if (!deleteUrl || deleteUrl === "#") return Promise.reject("Invalid delete URL"); + + return fetch(deleteUrl) + .then((response) => { + if (!response.ok) throw new Error("Delete request failed"); + return response; + }); +} + let tagDisplayRemovalInitialized = false; function initTagDisplayAndRemoval() { if (tagDisplayRemovalInitialized) return; @@ -1136,7 +1152,7 @@ function getElementVisualFontColor(element) { } function refreshNoteFilterVisuals() { - document.querySelectorAll(".note-card, .note-modal, .link-outer").forEach((element) => { + document.querySelectorAll(".note-card, .note-modal, .link-outer, .note-input-container").forEach((element) => { applyNoteVisualState(element, { color: getElementVisualColor(element), filter: getElementVisualFilter(element), @@ -1238,7 +1254,7 @@ function initThemeModeBackgroundSync() { if (nextTheme === lastTheme) return; lastTheme = nextTheme; - refreshNoteBackgroundVisuals(); + refreshNoteFilterVisuals(); refreshBackgroundPalettes(); }); @@ -1388,17 +1404,12 @@ function ensureBackgroundStudioPanel() { if (action === "set-defaults") { if (mode === "draft") { panelEl.dataset.color = "default"; - panelEl.dataset.filter = "none"; - panelEl.dataset.background = "none"; - panelEl.dataset.fontColor = "auto"; - applyDraft({ color: "default", filter: "none", background: "none", fontColor: "auto" }); + applyDraft({ color: "default" }); renderBackgroundStudioPanel(panelEl); } else if (mode === "modal") { setModalNoteColor("default"); - setModalNoteFilter("none"); } else { setNoteColor(entityId, "default", editUrl); - setNoteFilter(entityId, "none", editUrl); } return; } @@ -1814,7 +1825,7 @@ function applyNoteVisualState(element, note) { if (colorValue) { element.style.backgroundColor = colorValue; - element.style.borderColor = "transparent"; + element.style.removeProperty("border-color"); } else { element.style.removeProperty("background-color"); } @@ -2323,7 +2334,7 @@ function initTodoView(linkList, container) {
    - + @@ -2403,10 +2414,19 @@ function initTodoView(linkList, container) { }); modalOverlay.querySelector("#todo-modal-delete").addEventListener("click", () => { + if (!confirm("Supprimer cette tâche ?")) return; const modalCard = modalOverlay.querySelector(".note-modal"); const deleteUrl = modalCard.dataset.deleteUrl; + const todoId = modalCard.dataset.todoId; if (deleteUrl && deleteUrl !== "#") { - window.location.href = deleteUrl; + deleteEntitySilently(deleteUrl) + .then(() => { + window.location.href = "/?searchtags=shaarli-todo"; + }) + .catch((err) => { + console.error("Delete failed:", err); + alert("Erreur lors de la suppression."); + }); } }); @@ -2884,6 +2904,13 @@ function renderTodos(container, todos, viewMode) { const actions = document.createElement("div"); actions.className = "note-hover-actions"; + const pinCorner = document.createElement("a"); + pinCorner.href = todo.pinUrl; + pinCorner.title = todo.isPinned ? "Unpin" : "Pin"; + pinCorner.className = `note-pin-corner ${todo.isPinned ? "active" : ""}`; + pinCorner.innerHTML = ``; + inner.appendChild(pinCorner); + const paletteBtnId = `palette-${todo.id}`; actions.innerHTML = ` @@ -2891,11 +2918,25 @@ function renderTodos(container, todos, viewMode) {
    - - - + + `; + const deleteBtn = actions.querySelector(".todo-delete-btn"); + deleteBtn?.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + if (!confirm("Supprimer cette tâche ?")) return; + deleteEntitySilently(todo.deleteUrl) + .then(() => { + window.location.href = "/?searchtags=shaarli-todo"; + }) + .catch((err) => { + console.error("Delete failed:", err); + alert("Erreur lors de la suppression."); + }); + }); + const paletteBtn = actions.querySelector(`#${paletteBtnId}`); paletteBtn.addEventListener("click", (e) => { e.preventDefault(); @@ -3841,7 +3882,7 @@ function initNoteView(linkList, container) { - + @@ -3945,10 +3986,19 @@ function initNoteView(linkList, container) { }); modalOverlay.querySelector("#note-modal-delete").addEventListener("click", () => { + if (!confirm("Supprimer cette note ?")) return; const modalCard = modalOverlay.querySelector(".note-modal"); const deleteUrl = modalCard.dataset.deleteUrl; + const noteId = modalCard.dataset.noteId; if (deleteUrl && deleteUrl !== "#") { - window.location.href = deleteUrl; + deleteEntitySilently(deleteUrl) + .then(() => { + window.location.href = "/?searchtags=note"; + }) + .catch((err) => { + console.error("Delete failed:", err); + alert("Erreur lors de la suppression."); + }); } }); @@ -4046,7 +4096,7 @@ function initArchiveView(linkList, container) { const notes = links.map((link) => parseNoteFromLink(link)); // Filter only archived notes - const archivedNotes = notes.filter((note) => (note.tags || []).includes("shaarli-archive")); + const archivedNotes = notes.filter((note) => (note.tags || []).some((t) => t === "shaarli-archive" || t === "shaarli-archiver")); // Initial Render (Grid) renderNotes(contentArea, archivedNotes, "grid", true); // true = archive mode @@ -4085,7 +4135,7 @@ function initArchiveView(linkList, container) { - + @@ -4171,10 +4221,19 @@ function initArchiveView(linkList, container) { }); modalOverlay.querySelector("#note-modal-delete").addEventListener("click", () => { + if (!confirm("Supprimer cette note ?")) return; const modalCard = modalOverlay.querySelector(".note-modal"); const deleteUrl = modalCard.dataset.deleteUrl; + const noteId = modalCard.dataset.noteId; if (deleteUrl && deleteUrl !== "#") { - window.location.href = deleteUrl; + deleteEntitySilently(deleteUrl) + .then(() => { + window.location.href = "/?searchtags=shaarli-archive"; + }) + .catch((err) => { + console.error("Delete failed:", err); + alert("Erreur lors de la suppression."); + }); } }); @@ -4293,10 +4352,10 @@ function renderNotes(container, notes, viewMode, isArchiveMode = false) { let visibleNotes; if (isArchiveMode) { // In archive mode: show only notes with shaarli-archive tag - visibleNotes = notes.filter((note) => (note.tags || []).includes("shaarli-archive")); + visibleNotes = notes.filter((note) => (note.tags || []).some((t) => t === "shaarli-archive" || t === "shaarli-archiver")); } else { // In normal notes mode: hide archived notes - visibleNotes = notes.filter((note) => !(note.tags || []).includes("shaarli-archive")); + visibleNotes = notes.filter((note) => !(note.tags || []).some((t) => t === "shaarli-archive" || t === "shaarli-archiver")); } // Sort: Pinned items first @@ -4348,7 +4407,7 @@ function renderNotes(container, notes, viewMode, isArchiveMode = false) { if (note.descHtml || note.descText || note._noteMarkdown) { const body = document.createElement("div"); body.className = "note-body"; - + if (note._noteMarkdown !== undefined) { body.innerHTML = renderMarkdown(note._noteMarkdown); } else { @@ -4359,7 +4418,7 @@ function renderNotes(container, notes, viewMode, isArchiveMode = false) { body.innerHTML = renderMarkdown(textToRender); } } - + inner.appendChild(body); } @@ -4379,6 +4438,13 @@ function renderNotes(container, notes, viewMode, isArchiveMode = false) { const actions = document.createElement("div"); actions.className = "note-hover-actions"; + const pinCorner = document.createElement("a"); + pinCorner.href = note.pinUrl; + pinCorner.title = note.isPinned ? "Unpin" : "Pin"; + pinCorner.className = `note-pin-corner ${note.isPinned ? "active" : ""}`; + pinCorner.innerHTML = ``; + inner.appendChild(pinCorner); + // Palette Button Logic const paletteBtnId = `palette-${note.id}`; const archiveBtnId = `archive-${note.id}`; @@ -4393,11 +4459,25 @@ function renderNotes(container, notes, viewMode, isArchiveMode = false) {
    - - + `; + const deleteBtn = actions.querySelector(".note-delete-btn"); + deleteBtn?.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + if (!confirm("Supprimer cette note ?")) return; + deleteEntitySilently(note.deleteUrl) + .then(() => { + window.location.href = isArchiveMode ? "/?searchtags=shaarli-archive" : "/?searchtags=note"; + }) + .catch((err) => { + console.error("Delete failed:", err); + alert("Erreur lors de la suppression."); + }); + }); + const openEditorBtn = actions.querySelector(".note-open-editor-btn"); openEditorBtn?.addEventListener("click", (e) => { e.preventDefault(); @@ -5211,7 +5291,7 @@ function initPinnedItems() { document.addEventListener( "click", function (e) { - const btn = e.target.closest('a[href*="do=pin"], .note-hover-actions a[href*="pin"], .link-actions a[href*="pin"]'); + const btn = e.target.closest('a[href*="do=pin"], .note-hover-actions a[href*="pin"], .note-pin-corner[href*="pin"], .link-actions a[href*="pin"]'); if (btn) { e.preventDefault(); e.stopPropagation(); @@ -5496,15 +5576,15 @@ function openColorPickerPanel({ mode, entityId, editUrl, type }) { const bgPanel = document.getElementById("shaarli-bg-studio"); prevFontColorKey = bgPanel ? (bgPanel.dataset.fontColor || "auto") : "auto"; } else - if ((mode || "entity") === "modal") { - const modal = getOpenModalOverlay(); - const modalCard = modal ? modal.querySelector(".note-modal") : null; - prevFontColorKey = (modalCard && modalCard.dataset.fontColor) ? modalCard.dataset.fontColor : "auto"; - } else { - const noteCard = document.querySelector(`.note-card[data-id="${entityId}"]`); - const bookmarkCard = document.querySelector(`.link-outer[data-id="${entityId}"]`); - prevFontColorKey = (noteCard && noteCard.dataset.fontColor) ? noteCard.dataset.fontColor : ((bookmarkCard && bookmarkCard.dataset.fontColor) ? bookmarkCard.dataset.fontColor : "auto"); - } + if ((mode || "entity") === "modal") { + const modal = getOpenModalOverlay(); + const modalCard = modal ? modal.querySelector(".note-modal") : null; + prevFontColorKey = (modalCard && modalCard.dataset.fontColor) ? modalCard.dataset.fontColor : "auto"; + } else { + const noteCard = document.querySelector(`.note-card[data-id="${entityId}"]`); + const bookmarkCard = document.querySelector(`.link-outer[data-id="${entityId}"]`); + prevFontColorKey = (noteCard && noteCard.dataset.fontColor) ? noteCard.dataset.fontColor : ((bookmarkCard && bookmarkCard.dataset.fontColor) ? bookmarkCard.dataset.fontColor : "auto"); + } panel.dataset.prevFontColorKey = prevFontColorKey; } else { panel.dataset.prevFontColorKey = ""; @@ -5518,24 +5598,24 @@ function openColorPickerPanel({ mode, entityId, editUrl, type }) { prevColorKey = bgPanel.dataset.color || "default"; } } else - if ((mode || "entity") === "modal") { - const modal = getOpenModalOverlay(); - const modalCard = modal ? modal.querySelector(".note-modal") : null; - if (modalCard) { - const isCustom = modalCard.dataset.color === "custom"; - const cc = modalCard.dataset.customColor || ""; - prevColorKey = isCustom && cc ? `custom:${cc}` : getElementVisualColor(modalCard); + if ((mode || "entity") === "modal") { + const modal = getOpenModalOverlay(); + const modalCard = modal ? modal.querySelector(".note-modal") : null; + if (modalCard) { + const isCustom = modalCard.dataset.color === "custom"; + const cc = modalCard.dataset.customColor || ""; + prevColorKey = isCustom && cc ? `custom:${cc}` : getElementVisualColor(modalCard); + } + } else { + const noteCard = document.querySelector(`.note-card[data-id="${entityId}"]`); + const bookmarkCard = document.querySelector(`.link-outer[data-id="${entityId}"]`); + const el = noteCard || bookmarkCard; + if (el) { + const isCustom = el.dataset.color === "custom"; + const cc = el.dataset.customColor || ""; + prevColorKey = isCustom && cc ? `custom:${cc}` : getElementVisualColor(el); + } } - } else { - const noteCard = document.querySelector(`.note-card[data-id="${entityId}"]`); - const bookmarkCard = document.querySelector(`.link-outer[data-id="${entityId}"]`); - const el = noteCard || bookmarkCard; - if (el) { - const isCustom = el.dataset.color === "custom"; - const cc = el.dataset.customColor || ""; - prevColorKey = isCustom && cc ? `custom:${cc}` : getElementVisualColor(el); - } - } panel.dataset.prevColorKey = prevColorKey; } else { panel.dataset.prevColorKey = ""; @@ -5732,10 +5812,11 @@ function openColorPickerPanel({ mode, entityId, editUrl, type }) { setFromHex(defaultColor); } + panel.style.display = "block"; + panel.style.visibility = "hidden"; // Position the color picker panel positionColorPickerPanel(panel, mode); - - panel.style.display = "block"; + panel.style.visibility = "visible"; panel.classList.add("open"); panel.setAttribute("aria-hidden", "false"); } @@ -5751,20 +5832,26 @@ function positionColorPickerPanel(panel, mode) { if (bgPanel && bgPanel.classList.contains("open")) { const bgRect = bgPanel.getBoundingClientRect(); + const panelRect = panel.getBoundingClientRect(); + const panelWidth = Math.max(1, panelRect.width); + const panelHeight = Math.max(1, panelRect.height); // Position to the right of the bg-studio panel let left = bgRect.right + 10; let top = bgRect.top; // Check if it fits on the right - if (left + panel.offsetWidth > window.innerWidth - viewportPadding) { + if (left + panelWidth > window.innerWidth - viewportPadding) { // Position to the left instead - left = bgRect.left - panel.offsetWidth - 10; + left = bgRect.left - panelWidth - 10; } // Ensure it stays within viewport if (left < viewportPadding) left = viewportPadding; - if (top + panel.offsetHeight > window.innerHeight - viewportPadding) { - top = window.innerHeight - viewportPadding - panel.offsetHeight; + if (left + panelWidth > window.innerWidth - viewportPadding) { + left = window.innerWidth - viewportPadding - panelWidth; + } + if (top + panelHeight > window.innerHeight - viewportPadding) { + top = window.innerHeight - viewportPadding - panelHeight; } if (top < viewportPadding) top = viewportPadding; @@ -5775,8 +5862,10 @@ function positionColorPickerPanel(panel, mode) { } else { // Center in viewport if bg panel not available const panelRect = panel.getBoundingClientRect(); - const left = Math.max(viewportPadding, (window.innerWidth - panelRect.width) / 2); - const top = Math.max(viewportPadding, (window.innerHeight - panelRect.height) / 2); + const clampedWidth = Math.max(1, panelRect.width); + const clampedHeight = Math.max(1, panelRect.height); + const left = Math.max(viewportPadding, Math.min((window.innerWidth - clampedWidth) / 2, window.innerWidth - viewportPadding - clampedWidth)); + const top = Math.max(viewportPadding, Math.min((window.innerHeight - clampedHeight) / 2, window.innerHeight - viewportPadding - clampedHeight)); panel.style.left = `${Math.round(left)}px`; panel.style.top = `${Math.round(top)}px`; } @@ -5899,7 +5988,7 @@ function setNoteColorVisual(noteId, colorKey) { }); element.classList.add("note-color-custom"); element.style.backgroundColor = hex; - element.style.borderColor = "transparent"; + element.style.removeProperty("border-color"); element.dataset.color = "custom"; element.dataset.customColor = hex; const fg = getReadableForegroundForBackground(hex); @@ -6038,7 +6127,7 @@ function setModalCustomNoteColor(color) { const modalCard = modal.querySelector(".note-modal"); if (modalCard) { modalCard.style.backgroundColor = color; - modalCard.style.borderColor = "transparent"; + modalCard.style.removeProperty("border-color"); modalCard.dataset.color = "custom"; modalCard.dataset.customColor = color; } diff --git a/shaarli-pro/js/script.js b/shaarli-pro/js/script.js index be12a85..2427c04 100644 --- a/shaarli-pro/js/script.js +++ b/shaarli-pro/js/script.js @@ -1,4 +1,20 @@ document.addEventListener('DOMContentLoaded', () => { + // ===== Add Todo Button Handler (Android convention) ===== + const addTodoBtn = document.querySelector('.sidebar-add-todo'); + if (addTodoBtn) { + addTodoBtn.addEventListener('click', (e) => { + e.preventDefault(); + if (!window.ShaarItRules) { + console.warn('[shaarit] ShaarItRules not available, cannot generate todo URL'); + return; + } + const basePath = addTodoBtn.getAttribute('data-base-path') || ''; + const todoUrl = window.ShaarItRules.generateTodoUrl(); + const redirectUrl = `${basePath}/admin/shaare?post=${encodeURIComponent(todoUrl)}&tags=todo&title=%E2%9C%85%20`; + window.location.href = redirectUrl; + }); + } + // ===== Theme Toggle ===== const themeCheckbox = document.getElementById('theme-toggle-checkbox'); const themeIconLight = document.getElementById('theme-icon-light'); @@ -88,14 +104,19 @@ document.addEventListener('DOMContentLoaded', () => { const HIDDEN_TAGS_STORAGE_KEY = 'shaarli_hidden_tags'; // Default system tags that are hidden by default + // Harmonisé avec ShaarIt Android (PRESET_SYSTEM_TAGS). const DEFAULT_HIDDEN_TAGS = [ 'note', - 'shaarli-pin', + 'shaarli-note', + 'todo', 'shaarli-todo', + 'shaarli-pin', 'note-color-*', 'notebg-*', 'notefilter-*', + 'font-*', 'readitlater', + 'brain-dump', 'shaarli-archive' ]; @@ -1706,12 +1727,14 @@ document.addEventListener('DOMContentLoaded', () => { const syncNoteCheckbox = () => { if (!noteCheckbox) return; - noteCheckbox.checked = tags.some((tag) => /^note$/i.test(tag)); + // Reconnaît les conventions Android (`note`, `#note`) et legacy (`shaarli-note`). + noteCheckbox.checked = tags.some((tag) => /^(note|#note|shaarli-note)$/i.test(tag)); }; const syncTodoCheckbox = () => { if (!todoCheckbox) return; - todoCheckbox.checked = tags.some((tag) => /^shaarli-todo$/i.test(tag)); + // Reconnaît les conventions Android (`todo`, `#todo`) et legacy (`shaarli-todo`). + todoCheckbox.checked = tags.some((tag) => /^(todo|#todo|shaarli-todo)$/i.test(tag)); }; // Helper functions to manage note emoji in title @@ -1956,6 +1979,66 @@ document.addEventListener('DOMContentLoaded', () => { form.addEventListener('submit', commitInputValue); + // === Todo migration: shaarli-todo → todo + Android URL === + // Lors de la sauvegarde d'un todo, remplacer le tag legacy par la norme Android + // et générer une URL Android si vide ou legacy. + form.addEventListener('submit', () => { + try { + if (!window.ShaarItRules) return; + const urlField = form.querySelector('input[name="lf_url"]'); + const url = urlField ? (urlField.value || '').trim() : ''; + + // Vérifier si c'est un todo (tag shaarli-todo ou todo) + const hasTodoTag = tags.some((t) => /^(todo|shaarli-todo)$/i.test(t)); + if (!hasTodoTag) return; + + // 1. Remplacer shaarli-todo par todo + const hadLegacyTag = tags.some((t) => /^shaarli-todo$/i.test(t)); + if (hadLegacyTag) { + tags = tags.filter((t) => !/^shaarli-todo$/i.test(t)); + if (!tags.some((t) => /^todo$/i.test(t))) { + tags.push('todo'); + } + updateHiddenTags(); + } + + // 2. Générer URL Android si vide ou legacy + if (!url || /^https?:\/\/shaarli-todo/.test(url) || url === 'http://shaarli-todo') { + const newUrl = window.ShaarItRules.generateTodoUrl(); + urlField.value = newUrl; + } + } catch (e) { + console.warn('[shaarit] todo migration failed:', e); + } + }); + + // === Content-type auto-tagging (harmonisation ShaarIt Android) === + // À la soumission, détecte le type de contenu depuis l'URL et injecte + // les tags automatiques (video, podcast, radio, music, article, news, + // social, repository+dev, shopping, image, pdf...). + // Les tags déjà présents ne sont jamais dupliqués ; aucun tag n'est + // supprimé. Désactivé pour les notes/todos (URLs internes). + form.addEventListener('submit', () => { + try { + if (!window.ShaarItRules) return; + const urlField = form.querySelector('input[name="lf_url"]'); + const url = urlField ? (urlField.value || '').trim() : ''; + if (!url) return; + + // Ne pas auto-tagger les notes/todos (URLs internes reconnues par ShaarItRules). + const fakeLink = { url: url, tags: tags }; + if (window.ShaarItRules.isNote(fakeLink) || window.ShaarItRules.isTodo(fakeLink)) return; + + const detection = window.ShaarItRules.detectContentType(url); + if (!detection || !detection.tags || detection.tags.length === 0) return; + + tags = window.ShaarItRules.mergeAutoTags(tags, detection.tags); + updateHiddenTags(); + } catch (e) { + console.warn('[shaarit] content-type auto-tagging failed:', e); + } + }); + updateHiddenTags(); syncReadLaterCheckbox(); syncNoteCheckbox(); diff --git a/shaarli-pro/js/shaarit-rules.js b/shaarli-pro/js/shaarit-rules.js new file mode 100644 index 0000000..a14731a --- /dev/null +++ b/shaarli-pro/js/shaarit-rules.js @@ -0,0 +1,377 @@ +/** + * shaarit-rules.js + * + * Règles métier partagées avec l'application Android ShaarIt. + * Source de vérité unique pour : + * - la détection des notes / todos / épinglés ; + * - les tags techniques cachés par défaut ; + * - la détection de type de contenu (video, podcast, radio, music, article, + * news, social, repository/dev, shopping, image, pdf, document) ; + * - la génération d'URLs internes (notes / todos). + * + * Non destructif : toutes les conventions existantes du thème web continuent + * d'être reconnues en lecture. Les règles Android viennent s'y ajouter. + * + * Exposé sur `window.ShaarItRules`. + */ +(function (global) { + "use strict"; + + // --------------------------------------------------------------------------- + // Tags système (cachés par défaut côté UI) + // --------------------------------------------------------------------------- + + var PRESET_SYSTEM_TAGS = [ + { name: "note", desc: "Notes - Identifiant interne", hidden: true }, + { name: "shaarli-note", desc: "Notes - Alias legacy", hidden: true }, + { name: "todo", desc: "Tâches - Identifiant interne", hidden: true }, + { name: "shaarli-todo", desc: "Tâches - Alias legacy", hidden: true }, + { name: "shaarli-pin", desc: "Épinglé - Favoris en haut", hidden: true }, + { name: "note-color-*", desc: "Couleurs des notes (wildcard)", hidden: true }, + { name: "notebg-*", desc: "Fonds des notes (wildcard)", hidden: true }, + { name: "notefilter-*", desc: "Filtres des notes (wildcard)", hidden: true }, + { name: "font-*", desc: "Couleur de police (wildcard)", hidden: true }, + { name: "readitlater", desc: "À lire plus tard", hidden: true }, + { name: "brain-dump", desc: "Capture rapide d'idées", hidden: true }, + { name: "shaarli-archive", desc: "Archivé", hidden: false }, + { name: "shaarli-archiver",desc: "Archivé - Alias legacy", hidden: false } + ]; + + // --------------------------------------------------------------------------- + // Détection d'entités (note / todo / pin) - compatibilité Android + legacy + // --------------------------------------------------------------------------- + + function toLower(x) { return String(x || "").toLowerCase(); } + + function asTagArray(tags) { + if (Array.isArray(tags)) return tags.map(toLower); + if (typeof tags === "string") { + return tags.split(/[\s,|]+/).map(toLower).filter(Boolean); + } + return []; + } + + /** + * Détection d'une note. + * Compatible avec les règles Android : + * - URL `note://` (mais pas `note://todo-`) + * - URL `http://shaare` / `/shaare` + * - URL `https://shaarit.app/note/...` + * - Tag `note`, `#note`, `shaarli-note` + */ + function isNote(link) { + if (!link) return false; + var url = String(link.url || "").trim(); + var tags = asTagArray(link.tags); + + if (url) { + var u = url.toLowerCase(); + if (u.indexOf("note://") === 0 && u.indexOf("note://todo-") !== 0) return true; + if (u.indexOf("http://shaare") === 0) return true; + if (u.indexOf("/shaare") === 0) return true; + if (u.indexOf("https://shaarit.app/note/") === 0) return true; + if (u.indexOf("http://shaarit.app/note/") === 0) return true; + } + + for (var i = 0; i < tags.length; i++) { + if (tags[i] === "note" || tags[i] === "#note" || tags[i] === "shaarli-note") return true; + } + return false; + } + + /** + * Détection d'une tâche. + * Compatible avec les règles Android : + * - URL `note://todo-...` + * - URL `https://shaarit.app/todo/...` + * - URL `http://shaarli-todo` (legacy web) + * - Tag `todo`, `#todo`, `shaarli-todo` + */ + function isTodo(link) { + if (!link) return false; + var url = String(link.url || "").trim(); + var tags = asTagArray(link.tags); + + if (url) { + var u = url.toLowerCase(); + if (u.indexOf("note://todo-") === 0) return true; + if (u.indexOf("https://shaarit.app/todo/") === 0) return true; + if (u.indexOf("http://shaarit.app/todo/") === 0) return true; + if (u.indexOf("http://shaarli-todo") === 0) return true; + if (u.indexOf("https://shaarli-todo") === 0) return true; + } + + for (var i = 0; i < tags.length; i++) { + if (tags[i] === "todo" || tags[i] === "#todo" || tags[i] === "shaarli-todo") return true; + } + return false; + } + + /** Détection épinglé via tag `shaarli-pin`. */ + function isPinned(link) { + if (!link) return false; + var tags = asTagArray(link.tags); + return tags.indexOf("shaarli-pin") !== -1; + } + + /** Détection archive (tag `shaarli-archive` ou legacy `shaarli-archiver`). */ + function isArchived(link) { + if (!link) return false; + var tags = asTagArray(link.tags); + return tags.indexOf("shaarli-archive") !== -1 || tags.indexOf("shaarli-archiver") !== -1; + } + + // --------------------------------------------------------------------------- + // Génération d'URLs internes (style Android) + // --------------------------------------------------------------------------- + + function randomId() { + // Identifiant court type base36, 12 caractères. + if (global.crypto && global.crypto.getRandomValues) { + var arr = new Uint8Array(9); + global.crypto.getRandomValues(arr); + var out = ""; + for (var i = 0; i < arr.length; i++) { + out += ("0" + arr[i].toString(36)).slice(-2); + } + return out.substring(0, 12); + } + return (Date.now().toString(36) + Math.random().toString(36).slice(2, 10)).substring(0, 12); + } + + function generateNoteUrl() { + return "https://shaarit.app/note/" + randomId(); + } + + function generateTodoUrl() { + return "https://shaarit.app/todo/" + randomId(); + } + + // --------------------------------------------------------------------------- + // Détection de type de contenu (ContentType, règles Android) + // --------------------------------------------------------------------------- + + var CONTENT_TYPES = { + UNKNOWN: "unknown", + ARTICLE: "article", + VIDEO: "video", + PODCAST: "podcast", + IMAGE: "image", + PDF: "pdf", + REPOSITORY: "repository", + DOCUMENT: "document", + SOCIAL: "social", + SHOPPING: "shopping", + NEWSLETTER: "newsletter", + MUSIC: "music", + RADIO: "radio", + NEWS: "news" + }; + + // Tags auto-ajoutés par type de contenu (conformes règles Android UI). + var CONTENT_TYPE_TAGS = { + video: ["video"], + podcast: ["podcast"], + radio: ["radio"], + music: ["music"], + article: ["article"], + news: ["news"], + social: ["social"], + repository: ["repository", "dev"], + shopping: ["shopping"], + newsletter: ["newsletter"], + image: ["image"], + pdf: ["pdf"], + document: [], + unknown: [] + }; + + function parseHost(url) { + try { + var u = new URL(url); + return (u.hostname || "").toLowerCase(); + } catch (e) { + // Fallback très simple si URL invalide + var m = String(url || "").match(/^[a-z][a-z0-9+.-]*:\/\/([^/?#]+)/i); + return m ? m[1].toLowerCase() : ""; + } + } + + function hostContainsAny(host, list) { + for (var i = 0; i < list.length; i++) { + if (host.indexOf(list[i]) !== -1) return true; + } + return false; + } + + // --- Audio : RADIO > PODCAST > MUSIC --- + + var RADIO_HOSTS = [ + "playerservices.streamtheworld.com", "icecast", "shoutcast", + "fluxradios.com", "tunein.com", "radio.garden", "mytuner-radio.com", + "iheart.com", "onlineradiobox.com", "radio.net" + ]; + + function isRadio(url, host) { + if (/\.(m3u|m3u8|pls)(\?|$)/i.test(url)) return true; + if (hostContainsAny(host, RADIO_HOSTS)) return true; + if (host.indexOf("stream.") === 0 || host.indexOf("live.") === 0) return true; + if (host.indexOf("ici.radio-canada.ca") !== -1 || host.indexOf("radio-canada.ca") !== -1) { + if (/\/balados(\/|$)/i.test(url)) return false; + if (/\/(direct|premiere|audio-fil)(\/|$)/i.test(url)) return true; + } + return false; + } + + var PODCAST_HOSTS = [ + "podcasts.apple.com", "overcast.fm", "pocketcasts.com", "castbox.fm", + "stitcher.com", "acast.com", "anchor.fm", "libsyn.com", + "simplecast.com", "buzzsprout.com" + ]; + + function isPodcast(url, host) { + if (hostContainsAny(host, PODCAST_HOSTS)) return true; + if (/open\.spotify\.com\/(show|episode)\//i.test(url)) return true; + if (/\/balados(\/|$)|\/ohdio\/balados(\/|$)/i.test(url)) return true; + if (/\.(xml|rss)(\?|$)/i.test(url)) return true; + return false; + } + + var MUSIC_HOSTS = [ + "music.apple.com", "deezer.com", "tidal.com", "music.youtube.com", + "bandcamp.com", "soundcloud.com", "mixcloud.com", "beatport.com" + ]; + + function isMusic(url, host) { + if (hostContainsAny(host, MUSIC_HOSTS)) return true; + if (/open\.spotify\.com\/(track|album|artist|playlist)\//i.test(url)) return true; + return false; + } + + // --- Catégories web --- + + var VIDEO_HOSTS = ["youtube.com", "youtu.be", "vimeo.com", "dailymotion.com", "twitch.tv", "netflix.com"]; + function isVideo(host) { return hostContainsAny(host, VIDEO_HOSTS); } + + var SOCIAL_HOSTS = [ + "facebook.com", "instagram.com", "tiktok.com", "twitter.com", "x.com", + "linkedin.com", "reddit.com", "snapchat.com", "pinterest.com", "mastodon" + ]; + function isSocial(host) { return hostContainsAny(host, SOCIAL_HOSTS); } + + var REPO_HOSTS = ["github.com", "gitlab.com", "bitbucket.org", "stackoverflow.com"]; + function isRepository(host) { return hostContainsAny(host, REPO_HOSTS); } + + var SHOPPING_HOSTS = ["amazon", "ebay", "etsy.com", "aliexpress.com", "shopify"]; + function isShopping(host) { return hostContainsAny(host, SHOPPING_HOSTS); } + + var DOCUMENT_HOSTS = [ + "docs.google.com", "drive.google.com", "notion.so", "trello.com", + "jira", "confluence" + ]; + function isDocument(host) { return hostContainsAny(host, DOCUMENT_HOSTS); } + + var NEWS_HOSTS = [ + "news", "nytimes", "lemonde", "bbc", "cnn", "reuters", + "theguardian", "lefigaro" + ]; + function isNews(host) { return hostContainsAny(host, NEWS_HOSTS); } + + var NEWSLETTER_HOSTS = ["substack", "revue", "mailchimp"]; + function isNewsletter(host) { return hostContainsAny(host, NEWSLETTER_HOSTS); } + + var IMAGE_HOSTS = ["imgur.com", "flickr.com"]; + function isImageUrl(url, host) { + if (/\.(jpe?g|png|gif|webp|bmp|svg)(\?|$)/i.test(url)) return true; + return hostContainsAny(host, IMAGE_HOSTS); + } + + function isPdfUrl(url) { + return /\.pdf(\?|$)/i.test(url); + } + + /** + * Détecte le type de contenu d'une URL (règles Android). + * @returns {{type: string, tags: string[]}} + */ + function detectContentType(url) { + var raw = String(url || "").trim(); + if (!raw) return { type: CONTENT_TYPES.UNKNOWN, tags: [] }; + + // URLs internes : pas de détection + var low = raw.toLowerCase(); + if (low.indexOf("note://") === 0 || + low.indexOf("https://shaarit.app/note/") === 0 || + low.indexOf("https://shaarit.app/todo/") === 0 || + low.indexOf("http://shaare") === 0 || + low.indexOf("/shaare") === 0 || + low.indexOf("http://shaarli-todo") === 0) { + return { type: CONTENT_TYPES.UNKNOWN, tags: [] }; + } + + var host = parseHost(raw); + + // 1. Fichiers (priorité haute sur l'extension) + if (isPdfUrl(raw)) return { type: CONTENT_TYPES.PDF, tags: CONTENT_TYPE_TAGS.pdf }; + if (isImageUrl(raw, host))return { type: CONTENT_TYPES.IMAGE, tags: CONTENT_TYPE_TAGS.image }; + + // 2. Audio : RADIO > PODCAST > MUSIC + if (isRadio(raw, host)) return { type: CONTENT_TYPES.RADIO, tags: CONTENT_TYPE_TAGS.radio }; + if (isPodcast(raw, host)) return { type: CONTENT_TYPES.PODCAST, tags: CONTENT_TYPE_TAGS.podcast }; + if (isMusic(raw, host)) return { type: CONTENT_TYPES.MUSIC, tags: CONTENT_TYPE_TAGS.music }; + + // 3. Vidéo + if (isVideo(host)) return { type: CONTENT_TYPES.VIDEO, tags: CONTENT_TYPE_TAGS.video }; + + // 4. Plateformes spécifiques + if (isRepository(host)) return { type: CONTENT_TYPES.REPOSITORY, tags: CONTENT_TYPE_TAGS.repository }; + if (isDocument(host)) return { type: CONTENT_TYPES.DOCUMENT, tags: CONTENT_TYPE_TAGS.document }; + if (isSocial(host)) return { type: CONTENT_TYPES.SOCIAL, tags: CONTENT_TYPE_TAGS.social }; + if (isShopping(host)) return { type: CONTENT_TYPES.SHOPPING, tags: CONTENT_TYPE_TAGS.shopping }; + if (isNewsletter(host)) return { type: CONTENT_TYPES.NEWSLETTER, tags: CONTENT_TYPE_TAGS.newsletter }; + if (isNews(host)) return { type: CONTENT_TYPES.NEWS, tags: CONTENT_TYPE_TAGS.news }; + + return { type: CONTENT_TYPES.UNKNOWN, tags: [] }; + } + + /** + * Fusion d'un tableau de tags existants avec les tags auto-détectés. + * Conserve l'ordre existant, ajoute uniquement les tags manquants. + */ + function mergeAutoTags(existingTags, autoTags) { + var list = Array.isArray(existingTags) ? existingTags.slice() : []; + var lower = list.map(toLower); + (autoTags || []).forEach(function (t) { + var clean = String(t || "").trim(); + if (!clean) return; + if (lower.indexOf(clean.toLowerCase()) === -1) { + list.push(clean); + lower.push(clean.toLowerCase()); + } + }); + return list; + } + + // --------------------------------------------------------------------------- + // Export + // --------------------------------------------------------------------------- + + var api = { + PRESET_SYSTEM_TAGS: PRESET_SYSTEM_TAGS, + CONTENT_TYPES: CONTENT_TYPES, + CONTENT_TYPE_TAGS: CONTENT_TYPE_TAGS, + + isNote: isNote, + isTodo: isTodo, + isPinned: isPinned, + isArchived: isArchived, + + generateNoteUrl: generateNoteUrl, + generateTodoUrl: generateTodoUrl, + + detectContentType: detectContentType, + mergeAutoTags: mergeAutoTags + }; + + global.ShaarItRules = api; +})(typeof window !== "undefined" ? window : this); diff --git a/shaarli-pro/linklist.html b/shaarli-pro/linklist.html index 33dfb31..500e1f1 100644 --- a/shaarli-pro/linklist.html +++ b/shaarli-pro/linklist.html @@ -47,11 +47,16 @@ {if="count($links)==0"} + {if="in_array('note', $active_search_tags) || in_array('shaarli-note', $active_search_tags) || in_array('todo', $active_search_tags) || in_array('shaarli-todo', $active_search_tags) || in_array('shaarli-archive', $active_search_tags) || in_array('shaarli-archiver', $active_search_tags)"} + + {include="linklist.paging"} + {else}

    Aucun bookmark trouvé

    {if="!empty($search_term)"}Aucun résultat pour : {$search_term}{else}Commencez à ajouter des bookmarks pour les voir apparaître ici.{/if}

    + {/if} {else} @@ -154,7 +154,7 @@ Bookmarklet detection logic QUOTIDIEN - + TÂCHES diff --git a/shaarli-pro/tools.html b/shaarli-pro/tools.html index 8581a7a..f9d8af8 100644 --- a/shaarli-pro/tools.html +++ b/shaarli-pro/tools.html @@ -430,13 +430,19 @@ (function() { const STORAGE_KEY = 'shaarli_hidden_tags'; + // Harmonisé avec ShaarIt Android (PRESET_SYSTEM_TAGS). const PRESET_TAGS = [ { name: 'note', desc: 'Notes - Internal note identifier' }, + { name: 'shaarli-note', desc: 'Notes - Legacy alias' }, + { name: 'todo', desc: 'Tasks - Internal todo identifier' }, + { name: 'shaarli-todo', desc: 'Tasks - Legacy alias' }, { name: 'shaarli-pin', desc: 'Pinned - Keeps bookmarks at top' }, { name: 'note-color-*', desc: 'Note Colors - Wildcard for color tags' }, { name: 'notebg-*', desc: 'Note Backgrounds - Wildcard for background tags' }, { name: 'notefilter-*', desc: 'Note Filters - Wildcard for filter tags' }, + { name: 'font-*', desc: 'Note Font Colors - Wildcard' }, { name: 'readitlater', desc: 'Read Later - Temporary reading list' }, + { name: 'brain-dump', desc: 'Brain dump - Quick idea capture' }, { name: 'shaarli-archive', desc: 'Archived - Archived notes' } ];