feat: améliorer styles dark mode avec background unifié #20293a, bordures notes/cards (1px solid #000 light, #dedfe2 dark), width input 600px→800px, ajout bouton pin corner absolu (top-right, hover scale 1.06), suppression backgrounds/borders/outlines inputs (transparent + !important), nettoyage code dupliqué note-input-collapsed, styles todo-draft-row transparent, et corrections mineures formatage CSS (espaces, indentation)
This commit is contained in:
parent
ef6f9cb486
commit
f99c1cbecb
@ -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 {
|
||||
@ -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,7 +779,7 @@ body.note-modal-open {
|
||||
|
||||
[data-theme="dark"] .note-modal {
|
||||
background: #202124;
|
||||
border-color: transparent;
|
||||
border-color: #dedfe2;
|
||||
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.5), 0 10px 10px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,6 +2082,7 @@ 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 {
|
||||
@ -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,
|
||||
background-image: repeating-linear-gradient(transparent,
|
||||
transparent 29px,
|
||||
rgba(0, 0, 0, 0.1) 30px
|
||||
) !important;
|
||||
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,
|
||||
background-image: repeating-linear-gradient(transparent,
|
||||
transparent 29px,
|
||||
rgba(255, 255, 255, 0.1) 30px
|
||||
) !important;
|
||||
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,
|
||||
background-image: repeating-linear-gradient(45deg,
|
||||
transparent,
|
||||
transparent 10px,
|
||||
rgba(0, 0, 0, 0.05) 10px,
|
||||
rgba(0, 0, 0, 0.05) 20px
|
||||
) !important;
|
||||
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,
|
||||
background-image: repeating-linear-gradient(45deg,
|
||||
transparent,
|
||||
transparent 10px,
|
||||
rgba(255, 255, 255, 0.05) 10px,
|
||||
rgba(255, 255, 255, 0.05) 20px
|
||||
) !important;
|
||||
rgba(255, 255, 255, 0.05) 20px) !important;
|
||||
}
|
||||
|
||||
/* =========================================
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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')}
|
||||
|
||||
@ -67,6 +67,7 @@ visibility: '{$visibility}',
|
||||
untaggedonly: (function(){/*{if="$untaggedonly"}*/return true;/*{else}*/return false;/*{/if}*/})()
|
||||
};
|
||||
</script>
|
||||
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/js/shaarit-rules.js?v=1.0.0" defer></script>
|
||||
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/js/script.js?v=1.0.5" defer></script>
|
||||
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/js/custom_views.js?v=1.0.6" defer></script>
|
||||
|
||||
|
||||
@ -13,14 +13,18 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
// 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");
|
||||
@ -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) {
|
||||
<div class="palette-popup note-modal-palette" id="todo-modal-color-popup"></div>
|
||||
</div>
|
||||
<a href="#" id="todo-modal-edit" title="Modifier"><i class="mdi mdi-pencil-outline"></i></a>
|
||||
<button type="button" id="todo-modal-delete" title="Supprimer"><i class="mdi mdi-dots-vertical"></i></button>
|
||||
<button type="button" id="todo-modal-delete" title="Supprimer"><i class="mdi mdi-delete-outline"></i></button>
|
||||
</div>
|
||||
<button type="button" class="note-modal-close-btn" id="todo-modal-close">Fermer</button>
|
||||
</div>
|
||||
@ -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 = `<i class="mdi mdi-pin${todo.isPinned ? "" : "-outline"}"></i>`;
|
||||
inner.appendChild(pinCorner);
|
||||
|
||||
const paletteBtnId = `palette-${todo.id}`;
|
||||
|
||||
actions.innerHTML = `
|
||||
@ -2891,11 +2918,25 @@ function renderTodos(container, todos, viewMode) {
|
||||
<button title="Couleur" id="${paletteBtnId}"><i class="mdi mdi-palette-outline"></i></button>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<a href="${todo.pinUrl}" title="${todo.isPinned ? "Unpin" : "Pin"}" class="${todo.isPinned ? "active" : ""}"><i class="mdi mdi-pin${todo.isPinned ? "" : "-outline"}"></i></a>
|
||||
<a href="${todo.editUrl}" title="Edit"><i class="mdi mdi-pencil-outline"></i></a>
|
||||
<button title="Plus" onclick="window.location.href='${todo.deleteUrl}'"><i class="mdi mdi-dots-vertical"></i></button>
|
||||
<a href="${todo.editUrl}" title="Modifier"><i class="mdi mdi-pencil-outline"></i></a>
|
||||
<button title="Supprimer" class="todo-delete-btn"><i class="mdi mdi-delete-outline"></i></button>
|
||||
`;
|
||||
|
||||
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) {
|
||||
<button type="button" title="Image"><i class="mdi mdi-image-outline"></i></button>
|
||||
<button type="button" id="note-modal-archive" title="Archiver"><i class="mdi mdi-archive-arrow-down-outline"></i></button>
|
||||
<a href="#" id="note-modal-edit" title="Modifier"><i class="mdi mdi-pencil-outline"></i></a>
|
||||
<button type="button" id="note-modal-delete" title="Supprimer"><i class="mdi mdi-dots-vertical"></i></button>
|
||||
<button type="button" id="note-modal-delete" title="Supprimer"><i class="mdi mdi-delete-outline"></i></button>
|
||||
</div>
|
||||
<button type="button" class="note-modal-close-btn" id="note-modal-close">Fermer</button>
|
||||
</div>
|
||||
@ -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) {
|
||||
<button type="button" title="Image"><i class="mdi mdi-image-outline"></i></button>
|
||||
<button type="button" id="note-modal-unarchive" title="Désarchiver"><i class="mdi mdi-archive-arrow-up-outline"></i></button>
|
||||
<a href="#" id="note-modal-edit" title="Modifier"><i class="mdi mdi-pencil-outline"></i></a>
|
||||
<button type="button" id="note-modal-delete" title="Supprimer"><i class="mdi mdi-dots-vertical"></i></button>
|
||||
<button type="button" id="note-modal-delete" title="Supprimer"><i class="mdi mdi-delete-outline"></i></button>
|
||||
</div>
|
||||
<button type="button" class="note-modal-close-btn" id="note-modal-close">Fermer</button>
|
||||
</div>
|
||||
@ -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
|
||||
@ -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 = `<i class="mdi mdi-pin${note.isPinned ? "" : "-outline"}"></i>`;
|
||||
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) {
|
||||
<button title="Archiver" id="${archiveBtnId}"><i class="mdi mdi-archive-arrow-down-outline"></i></button>
|
||||
<div class="spacer"></div>
|
||||
<!-- Real Actions -->
|
||||
<a href="${note.pinUrl}" title="${note.isPinned ? "Unpin" : "Pin"}" class="${note.isPinned ? "active" : ""}"><i class="mdi mdi-pin${note.isPinned ? "" : "-outline"}"></i></a>
|
||||
<button type="button" class="note-open-editor-btn" title="Modifier"><i class="mdi mdi-pencil-outline"></i></button>
|
||||
<button title="Plus" onclick="window.location.href='${note.deleteUrl}'"><i class="mdi mdi-dots-vertical"></i></button>
|
||||
<button title="Supprimer" class="note-delete-btn"><i class="mdi mdi-delete-outline"></i></button>
|
||||
`;
|
||||
|
||||
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();
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
377
shaarli-pro/js/shaarit-rules.js
Normal file
377
shaarli-pro/js/shaarit-rules.js
Normal file
@ -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);
|
||||
@ -47,11 +47,16 @@
|
||||
|
||||
<!-- {* ----- no links ----- *} -->
|
||||
{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)"}
|
||||
<div class="links-list view-grid" id="links-list"></div>
|
||||
{include="linklist.paging"}
|
||||
{else}
|
||||
<div class="empty-state" role="status" aria-live="polite">
|
||||
<div class="empty-state-icon" aria-hidden="true"><i class="mdi mdi-bookmark-off-outline"></i></div>
|
||||
<h2 class="empty-state-title">Aucun bookmark trouvé</h2>
|
||||
<p class="empty-state-text">{if="!empty($search_term)"}Aucun résultat pour : <strong>{$search_term}</strong>{else}Commencez à ajouter des bookmarks pour les voir apparaître ici.{/if}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{else}
|
||||
<!-- {* ----- at least one link ----- *} -->
|
||||
<div class="links-list view-grid" id="links-list">
|
||||
|
||||
104
shaarli-pro/migrate-todos.php
Normal file
104
shaarli-pro/migrate-todos.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/**
|
||||
* migrate-todos.php
|
||||
*
|
||||
* Script de migration batch : remplace tous les todos legacy (shaarli-todo)
|
||||
* par la norme Android (tag `todo` + URL `https://shaarit.app/todo/{uuid}`).
|
||||
*
|
||||
* À exécuter une seule fois via CLI ou interface web (si accessible).
|
||||
* Usage: php migrate-todos.php
|
||||
*/
|
||||
|
||||
// Déterminer le chemin racine de Shaarli
|
||||
$rootDir = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $rootDir . '/index.php';
|
||||
|
||||
// Vérifier que Shaarli est chargé
|
||||
if (!isset($GLOBALS['shaarli'])) {
|
||||
die("Erreur : Shaarli non chargé.\n");
|
||||
}
|
||||
|
||||
$linkDb = $GLOBALS['shaarli']->getContainer()->get('db');
|
||||
if (!$linkDb) {
|
||||
die("Erreur : base de données non accessible.\n");
|
||||
}
|
||||
|
||||
// Récupérer tous les liens avec le tag shaarli-todo
|
||||
$allLinks = $linkDb->getLinks();
|
||||
$todosToMigrate = [];
|
||||
|
||||
foreach ($allLinks as $link) {
|
||||
$tags = explode(' ', $link->getTags());
|
||||
$hasTodoTag = in_array('shaarli-todo', $tags, true);
|
||||
if ($hasTodoTag) {
|
||||
$todosToMigrate[] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($todosToMigrate)) {
|
||||
echo "Aucun todo à migrer.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
echo "Trouvé " . count($todosToMigrate) . " todo(s) à migrer.\n";
|
||||
|
||||
// Fonction pour générer un UUID court (style Android)
|
||||
function generateTodoUuid() {
|
||||
if (function_exists('random_bytes')) {
|
||||
$bytes = random_bytes(9);
|
||||
$out = '';
|
||||
for ($i = 0; $i < 9; $i++) {
|
||||
$out .= str_pad(base_convert(ord($bytes[$i]), 10, 36), 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
return substr($out, 0, 12);
|
||||
}
|
||||
return substr(base_convert(time(), 10, 36) . base_convert(mt_rand(), 10, 36), 0, 12);
|
||||
}
|
||||
|
||||
// Migrer chaque todo
|
||||
$migrated = 0;
|
||||
$errors = 0;
|
||||
|
||||
foreach ($todosToMigrate as $link) {
|
||||
try {
|
||||
$tags = explode(' ', $link->getTags());
|
||||
|
||||
// 1. Remplacer shaarli-todo par todo
|
||||
$tags = array_filter($tags, function ($t) {
|
||||
return $t !== 'shaarli-todo';
|
||||
});
|
||||
$tags = array_values($tags);
|
||||
if (!in_array('todo', $tags, true)) {
|
||||
$tags[] = 'todo';
|
||||
}
|
||||
$link->setTags($tags);
|
||||
|
||||
// 2. Générer URL Android si vide ou legacy
|
||||
$url = $link->getUrl();
|
||||
if (empty($url) || strpos($url, 'http://shaarli-todo') === 0 || strpos($url, 'https://shaarli-todo') === 0) {
|
||||
$uuid = generateTodoUuid();
|
||||
$link->setUrl('https://shaarit.app/todo/' . $uuid);
|
||||
}
|
||||
|
||||
// Sauvegarder
|
||||
$linkDb->save($link);
|
||||
$migrated++;
|
||||
echo "✓ Migré : {$link->getTitle()} ({$link->getId()})\n";
|
||||
} catch (Exception $e) {
|
||||
$errors++;
|
||||
echo "✗ Erreur : {$link->getTitle()} - {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n=== Résumé ===\n";
|
||||
echo "Migrés : $migrated\n";
|
||||
echo "Erreurs : $errors\n";
|
||||
echo "Total : " . count($todosToMigrate) . "\n";
|
||||
|
||||
if ($errors === 0) {
|
||||
echo "\n✓ Migration terminée avec succès !\n";
|
||||
exit(0);
|
||||
} else {
|
||||
echo "\n⚠ Migration terminée avec $errors erreur(s).\n";
|
||||
exit(1);
|
||||
}
|
||||
@ -49,7 +49,7 @@ Bookmarklet detection logic
|
||||
<i class="mdi mdi-calendar-today" aria-hidden="true"></i>
|
||||
<span>Quotidien</span>
|
||||
</a>
|
||||
<a href="{$base_path}/?searchtags=shaarli-todo" class="sidebar-link{if="isset($search_tags) && preg_match('/(^|[\s,])shaarli-todo([\s,]|$)/i', (string) $search_tags)"} active{/if}" aria-label="Mes tâches">
|
||||
<a href="{$base_path}/?searchtags=todo" class="sidebar-link{if="isset($search_tags) && preg_match('/(^|[\s,])todo([\s,]|$)/i', (string) $search_tags)"} active{/if}" aria-label="Mes tâches">
|
||||
<i class="mdi mdi-check-circle-outline" aria-hidden="true"></i>
|
||||
<span>Mes tâches</span>
|
||||
</a>
|
||||
@ -93,7 +93,7 @@ Bookmarklet detection logic
|
||||
<a href="{$base_path}/admin/shaare?tags=note" class="sidebar-add-segment" title="Note">
|
||||
<i class="mdi mdi-note-text-outline"></i>
|
||||
</a>
|
||||
<a href="{$base_path}/admin/shaare?post=http%3A%2F%2Fshaarli-todo&tags=shaarli-todo&title=%E2%9C%85%20" class="sidebar-add-segment" title="Todo">
|
||||
<a href="#" class="sidebar-add-segment sidebar-add-todo" title="Todo" data-base-path="{$base_path}">
|
||||
<i class="mdi mdi-check-circle-outline"></i>
|
||||
</a>
|
||||
</div>
|
||||
@ -154,7 +154,7 @@ Bookmarklet detection logic
|
||||
<i class="mdi mdi-calendar" aria-hidden="true"></i>
|
||||
<span>QUOTIDIEN</span>
|
||||
</a>
|
||||
<a href="{$base_path}/?searchtags=shaarli-todo" class="header-nav-link{if="isset($search_tags) && preg_match('/(^|[\s,])shaarli-todo([\s,]|$)/i', (string) $search_tags)"} active{/if}" aria-label="Mes tâches">
|
||||
<a href="{$base_path}/?searchtags=todo" class="header-nav-link{if="isset($search_tags) && preg_match('/(^|[\s,])todo([\s,]|$)/i', (string) $search_tags)"} active{/if}" aria-label="Mes tâches">
|
||||
<i class="mdi mdi-check-circle-outline" aria-hidden="true"></i>
|
||||
<span>TÂCHES</span>
|
||||
</a>
|
||||
|
||||
@ -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' }
|
||||
];
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user