diff --git a/shaarli-pro/css/custom_views.css b/shaarli-pro/css/custom_views.css
index 6f7efdc..0c7b847 100644
--- a/shaarli-pro/css/custom_views.css
+++ b/shaarli-pro/css/custom_views.css
@@ -1557,4 +1557,177 @@ body.view-notes .content-container {
background-size: cover;
background-position: center bottom;
}
+}
+
+/* =========================================
+ Note Filter Effects (8 CSS filters)
+ ========================================= */
+
+/* 1. Glassmorphism - Effet verre givré */
+.note-card.note-filter-glass,
+.note-modal.note-filter-glass,
+.link-outer.note-filter-glass {
+ background: rgba(255, 255, 255, 0.25) !important;
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.3);
+}
+
+[data-theme="dark"] .note-card.note-filter-glass,
+[data-theme="dark"] .note-modal.note-filter-glass,
+[data-theme="dark"] .link-outer.note-filter-glass {
+ background: rgba(0, 0, 0, 0.35) !important;
+ border: 1px solid rgba(255, 255, 255, 0.15);
+}
+
+/* 2. Vignette - Coins sombres */
+.note-card.note-filter-vignette::after,
+.note-modal.note-filter-vignette::after,
+.link-outer.note-filter-vignette::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ border-radius: inherit;
+ pointer-events: none;
+ background: radial-gradient(circle, transparent 50%, rgba(0, 0, 0, 0.4) 100%);
+ z-index: 1;
+}
+
+.note-card.note-filter-vignette,
+.note-modal.note-filter-vignette,
+.link-outer.note-filter-vignette {
+ position: relative;
+}
+
+/* 3. Ligné - Papier ligné */
+.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;
+}
+
+[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;
+}
+
+/* 4. Quadrillé - Grid */
+.note-card.note-filter-grid,
+.note-modal.note-filter-grid,
+.link-outer.note-filter-grid {
+ background-image:
+ linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px) !important;
+ background-size: 20px 20px !important;
+}
+
+[data-theme="dark"] .note-card.note-filter-grid,
+[data-theme="dark"] .note-modal.note-filter-grid,
+[data-theme="dark"] .link-outer.note-filter-grid {
+ background-image:
+ linear-gradient(rgba(255, 255, 255, 0.1) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(255, 255, 255, 0.1) 1px, transparent 1px) !important;
+}
+
+/* 5. Noise - Texture granuleuse */
+.note-card.note-filter-noise,
+.note-modal.note-filter-noise,
+.link-outer.note-filter-noise {
+ position: relative;
+}
+
+.note-card.note-filter-noise::before,
+.note-modal.note-filter-noise::before,
+.link-outer.note-filter-noise::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ border-radius: inherit;
+ pointer-events: none;
+ opacity: 0.5;
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
+ mix-blend-mode: overlay;
+ z-index: 1;
+}
+
+/* 6. Points - Bullet journal dotted */
+.note-card.note-filter-dots,
+.note-modal.note-filter-dots,
+.link-outer.note-filter-dots {
+ background-image: radial-gradient(rgba(0, 0, 0, 0.2) 1px, transparent 1px) !important;
+ background-size: 20px 20px !important;
+}
+
+[data-theme="dark"] .note-card.note-filter-dots,
+[data-theme="dark"] .note-modal.note-filter-dots,
+[data-theme="dark"] .link-outer.note-filter-dots {
+ background-image: radial-gradient(rgba(255, 255, 255, 0.2) 1px, transparent 1px) !important;
+}
+
+/* 7. Rayures - Stripes diagonales */
+.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;
+}
+
+[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;
+}
+
+/* =========================================
+ Filter-specific row layout for palette
+ ========================================= */
+.palette-row-filters {
+ display: grid;
+ grid-template-columns: repeat(4, 28px);
+ gap: 10px;
+ justify-content: start;
+}
+
+.palette-btn-filter,
+.palette-btn-filter-none {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(15, 23, 42, 0.05);
+}
+
+.palette-btn-filter i,
+.palette-btn-filter-none i {
+ font-size: 1rem;
+ color: var(--text-color, #334155);
+}
+
+[data-theme="dark"] .palette-btn-filter,
+[data-theme="dark"] .palette-btn-filter-none {
+ background: rgba(255, 255, 255, 0.1);
+}
+
+[data-theme="dark"] .palette-btn-filter i,
+[data-theme="dark"] .palette-btn-filter-none i {
+ color: #e8eaed;
}
\ No newline at end of file
diff --git a/shaarli-pro/js/custom_views.js b/shaarli-pro/js/custom_views.js
index 5df2183..1676a31 100644
--- a/shaarli-pro/js/custom_views.js
+++ b/shaarli-pro/js/custom_views.js
@@ -119,6 +119,18 @@ const NOTE_COLOR_OPTIONS = [
}
];
+const NOTE_FILTER_OPTIONS = [
+ { key: "none", label: "Aucun", icon: "mdi-close-circle-outline" },
+ { key: "glass", label: "Glass", icon: "mdi-blur" },
+ { key: "vignette", label: "Vignette", icon: "mdi-vignette" },
+ { key: "lined", label: "Ligné", icon: "mdi-format-align-justify" },
+ { key: "grid", label: "Quadrillé", icon: "mdi-grid" },
+ { key: "noise", label: "Noise", icon: "mdi-grain" },
+ { key: "dots", label: "Points", icon: "mdi-dots-grid" },
+ { key: "stripes", label: "Rayures", icon: "mdi-slash-forward" },
+];
+
+const NOTE_FILTER_TAG_PREFIX = "notefilter-";
const NOTE_BACKGROUND_TAG_PREFIX = "notebg-";
function resolveThemeAssetBasePath() {
@@ -361,11 +373,25 @@ function getElementVisualBackground(element) {
return normalizedBackground || "none";
}
-function refreshNoteBackgroundVisuals() {
+function normalizeFilterKey(key) {
+ if (typeof key !== "string") return "none";
+ const normalized = key.trim().toLowerCase();
+ if (NOTE_FILTER_OPTIONS.some((opt) => opt.key === normalized)) {
+ return normalized;
+ }
+ return "none";
+}
+
+function getElementVisualFilter(element) {
+ if (!element) return "none";
+ return normalizeFilterKey(element.dataset.filter || "");
+}
+
+function refreshNoteFilterVisuals() {
document.querySelectorAll(".note-card, .note-modal, .link-outer").forEach((element) => {
applyNoteVisualState(element, {
color: getElementVisualColor(element),
- background: getElementVisualBackground(element),
+ filter: getElementVisualFilter(element),
});
});
}
@@ -566,8 +592,9 @@ function ensureBackgroundStudioPanel() {
}
if (action === "set-filter") {
- panelEl.dataset.filter = actionBtn.dataset.filter || "all";
- renderBackgroundStudioPanel(panelEl);
+ const filterKey = actionBtn.dataset.filter || "none";
+ if (mode === "modal") setModalNoteFilter(filterKey);
+ else setNoteFilter(entityId, filterKey, editUrl);
return;
}
@@ -582,10 +609,10 @@ function ensureBackgroundStudioPanel() {
if (action === "set-defaults") {
if (mode === "modal") {
setModalNoteColor("default");
- setModalNoteBackground("none");
+ setModalNoteFilter("none");
} else {
setNoteColor(entityId, "default", editUrl);
- setNoteBackground(entityId, "none", editUrl);
+ setNoteFilter(entityId, "none", editUrl);
}
return;
}
@@ -680,6 +707,7 @@ function openBackgroundStudioPanel({
entityId,
editUrl,
currentColor,
+ currentFilter,
currentBackground,
title,
}) {
@@ -691,8 +719,8 @@ function openBackgroundStudioPanel({
panel.dataset.entityId = entityId || "";
panel.dataset.editUrl = editUrl || "";
panel.dataset.color = currentColor || "default";
+ panel.dataset.filter = normalizeFilterKey(currentFilter || "") || "none";
panel.dataset.background = normalizeBackgroundKey(currentBackground || "") || "none";
- panel.dataset.filter = panel.dataset.filter || "all";
panel.dataset.query = panel.dataset.query || "";
panel.dataset.title = title || "Mes images & couleurs";
panel.__anchorEl = anchorEl || null;
@@ -813,37 +841,22 @@ function renderBackgroundStudioPanel(panel) {
const title = panel.dataset.title || "Mes images & couleurs";
const color = panel.dataset.color || "default";
- const background = normalizeBackgroundKey(panel.dataset.background || "") || "none";
- const filter = panel.dataset.filter || "all";
+ const currentFilter = panel.dataset.filter || "none";
const query = normalizeSearchText(panel.dataset.query || "");
const { colors, backgrounds } = getBackgroundStudioItems();
- const filterMatch = (item) => {
- if (filter === "all") return true;
- if (filter === "color") return item.type === "color";
- if (item.type !== "background") return false;
- if (filter === "solid") return item.category === "solid";
- if (filter === "gradient") return item.category === "gradient";
- if (filter === "grid") return item.category === "grid";
- if (filter === "image") return item.category === "image";
- if (filter === "texture") return item.category === "texture";
- return true;
- };
-
- const queryMatch = (item) => {
+ // Filter backgrounds based on search query
+ const filteredBackgrounds = backgrounds.filter((b) => {
if (!query) return true;
- const hay = normalizeSearchText([item.label, ...(item.keywords || [])].join(" "));
+ const hay = normalizeSearchText([b.label, b.key, "image", "background"].join(" "));
return hay.includes(query);
- };
+ });
- const galleryItems = backgrounds
- .filter((b) => filterMatch(b) && queryMatch(b))
- .map((b) => ({ ...b, kind: "background" }));
-
- const galleryHtml = galleryItems
+ // Generate gallery HTML for backgrounds
+ const galleryHtml = filteredBackgrounds
.map((item) => {
- const isActive = background === item.key;
+ const isActive = panel.dataset.background === item.key;
const common = `class="bg-studio-thumb ${isActive ? "is-active" : ""}" type="button"`;
const thumb =
item.key === "none"
@@ -864,17 +877,11 @@ function renderBackgroundStudioPanel(panel) {
.join("");
const filterBtn = (key, icon, label) => {
- const active = filter === key;
+ const active = currentFilter === key;
return ``;
};
- const filtersHtml = [
- filterBtn("solid", "mdi-checkbox-blank-outline", "Couleur unie"),
- filterBtn("gradient", "mdi-gradient-horizontal", "Dégradé"),
- filterBtn("grid", "mdi-grid", "Motif grille"),
- filterBtn("image", "mdi-image-outline", "Image personnalisée"),
- filterBtn("texture", "mdi-checkerboard", "Texture"),
- ].join("");
+ const filtersHtml = NOTE_FILTER_OPTIONS.map((f) => filterBtn(f.key, f.icon, f.label)).join("");
const showClear = !!(panel.dataset.query || "").trim();
@@ -893,13 +900,13 @@ function renderBackgroundStudioPanel(panel) {
${colorsRowHtml}
-
Backgrounds:
+
Filtres:
${filtersHtml}
-
+
`;
@@ -929,13 +936,18 @@ function applyNoteVisualState(element, note) {
const color = resolvedColorOption ? resolvedColorOption.key : "default";
const colorValue = getThemeColorValue(resolvedColorOption);
const foregroundColor = getReadableForegroundForBackground(colorValue);
+ const filter = normalizeFilterKey(note.filter || "none");
const normalizedBackground = normalizeBackgroundKey(note.background || "");
const background = normalizedBackground || "none";
element.classList.forEach((cls) => {
if (cls.startsWith("note-color-")) element.classList.remove(cls);
+ if (cls.startsWith("note-filter-")) element.classList.remove(cls);
});
element.classList.add(`note-color-${color}`);
+ if (filter !== "none") {
+ element.classList.add(`note-filter-${filter}`);
+ }
if (colorValue) {
element.style.backgroundColor = colorValue;
@@ -966,6 +978,7 @@ function applyNoteVisualState(element, note) {
}
element.dataset.color = color;
+ element.dataset.filter = filter;
}
function extractNoteVisualStateFromTags(tags) {
@@ -980,6 +993,15 @@ function extractNoteVisualStateFromTags(tags) {
}
}
+ let filter = "none";
+ const foundFilterTag = safeTags.find((t) => typeof t === "string" && t.startsWith(NOTE_FILTER_TAG_PREFIX));
+ if (foundFilterTag) {
+ const candidate = normalizeFilterKey(foundFilterTag.substring(NOTE_FILTER_TAG_PREFIX.length));
+ if (candidate && candidate !== "none") {
+ filter = candidate;
+ }
+ }
+
let background = "none";
const foundBgTag = safeTags.find((t) => typeof t === "string" && t.startsWith(NOTE_BACKGROUND_TAG_PREFIX));
if (foundBgTag) {
@@ -989,7 +1011,7 @@ function extractNoteVisualStateFromTags(tags) {
}
}
- return { color, background };
+ return { color, filter, background };
}
function initBookmarkPaletteButtons() {
@@ -1001,8 +1023,8 @@ function initBookmarkPaletteButtons() {
if (!cardId) return;
const tags = Array.from(card.querySelectorAll(".link-tag a")).map((a) => (a.textContent || "").trim());
- const { color, background } = extractNoteVisualStateFromTags(tags);
- applyNoteVisualState(card, { color, background });
+ const { color, filter, background } = extractNoteVisualStateFromTags(tags);
+ applyNoteVisualState(card, { color, filter, background });
const actions = card.querySelector(".link-actions");
if (!actions) return;
@@ -1042,6 +1064,7 @@ function initBookmarkPaletteButtons() {
entityId: cardId,
editUrl,
currentColor: getElementVisualColor(card),
+ currentFilter: getElementVisualFilter(card),
currentBackground: getElementVisualBackground(card),
title: "Mes images & couleurs",
});
@@ -1055,29 +1078,29 @@ function initBookmarkPaletteButtons() {
});
}
-function generateBookmarkPaletteButtons(bookmarkId, editUrl, currentColor, currentBackground) {
+function generateBookmarkPaletteButtons(bookmarkId, editUrl, currentColor, currentFilter) {
return generateUnifiedPaletteMenu({
entityId: bookmarkId,
editUrl,
currentColor,
- currentBackground,
+ currentFilter,
mode: "entity",
});
}
-function generateUnifiedPaletteMenu({ entityId, editUrl, currentColor, currentBackground, mode }) {
+function generateUnifiedPaletteMenu({ entityId, editUrl, currentColor, currentFilter, mode }) {
const color = currentColor || "default";
- const background = normalizeBackgroundKey(currentBackground || "") || "none";
+ const filter = normalizeFilterKey(currentFilter || "") || "none";
const onColorClick =
mode === "modal"
? (c) => `setModalNoteColor('${c}')`
: (c) => `setNoteColor('${entityId}', '${c}', '${editUrl}')`;
- const onBackgroundClick =
+ const onFilterClick =
mode === "modal"
- ? (k) => `setModalNoteBackground('${k}')`
- : (k) => `setNoteBackground('${entityId}', '${k}', '${editUrl}')`;
+ ? (k) => `setModalNoteFilter('${k}')`
+ : (k) => `setNoteFilter('${entityId}', '${k}', '${editUrl}')`;
const colorButtons = [
``,
@@ -1087,11 +1110,10 @@ function generateUnifiedPaletteMenu({ entityId, editUrl, currentColor, currentBa
}),
].join("");
- const backgroundButtons = [
- ``,
- ...getAvailableBackgroundOptionsForMode().map((bg) => {
- const bgUrl = getNoteBackgroundUrl(bg.key);
- return ``;
+ const filterButtons = [
+ ``,
+ ...NOTE_FILTER_OPTIONS.filter((opt) => opt.key !== "none").map((opt) => {
+ return ``;
}),
].join("");
@@ -1102,8 +1124,8 @@ function generateUnifiedPaletteMenu({ entityId, editUrl, currentColor, currentBa
-
Images
-
${backgroundButtons}
+
Filtres
+
${filterButtons}
`;
}
@@ -1116,6 +1138,9 @@ function syncNoteFromCardElement(note, card) {
note.color = colorClass.replace("note-color-", "") || "default";
}
+ const filter = card.dataset.filter;
+ note.filter = filter && filter !== "none" ? filter : "none";
+
const background = card.dataset.background;
note.background = background && background !== "none" ? background : "none";
}
@@ -1470,6 +1495,7 @@ function initNoteView(linkList, container) {
entityId: modalCard ? modalCard.dataset.noteId || "" : "",
editUrl: modalCard ? modalCard.dataset.editUrl || "" : "",
currentColor: modalCard ? getElementVisualColor(modalCard) : "default",
+ currentFilter: modalCard ? getElementVisualFilter(modalCard) : "none",
currentBackground: modalCard ? getElementVisualBackground(modalCard) : "none",
title: "Mes images & couleurs",
});
@@ -1587,6 +1613,7 @@ function parseNoteFromLink(linkEl) {
const tags = [];
let color = "default";
+ let filter = "none";
let background = "none";
linkEl.querySelectorAll(".link-tag-list a").forEach((tag) => {
const t = tag.textContent.trim();
@@ -1599,6 +1626,8 @@ function parseNoteFromLink(linkEl) {
} else {
tags.push(t);
}
+ } else if (t.startsWith(NOTE_FILTER_TAG_PREFIX)) {
+ filter = normalizeFilterKey(t.substring(NOTE_FILTER_TAG_PREFIX.length)) || "none";
} else if (t.startsWith(NOTE_BACKGROUND_TAG_PREFIX)) {
background = normalizeBackgroundKey(t.substring(NOTE_BACKGROUND_TAG_PREFIX.length)) || "none";
} else {
@@ -1616,7 +1645,7 @@ function parseNoteFromLink(linkEl) {
// User requested "availability of the tag 'shaarli-pin' as the main source"
const isPinned = tags.includes("shaarli-pin");
- return { id, title, descHtml, descText, coverImage, url, tags, color, background, editUrl, deleteUrl, pinUrl, isPinned };
+ return { id, title, descHtml, descText, coverImage, url, tags, color, filter, background, editUrl, deleteUrl, pinUrl, isPinned };
}
function renderNotes(container, notes, viewMode) {
@@ -1726,6 +1755,7 @@ function renderNotes(container, notes, viewMode) {
entityId: note.id,
editUrl: note.editUrl,
currentColor: getElementVisualColor(card),
+ currentFilter: getElementVisualFilter(card),
currentBackground: getElementVisualBackground(card),
title: "Mes images & couleurs",
});
@@ -1817,7 +1847,7 @@ function generateModalPaletteButtons(note) {
entityId: note && note.id ? note.id : "",
editUrl: note && note.editUrl ? note.editUrl : "",
currentColor: note && note.color ? note.color : "default",
- currentBackground: note && note.background ? note.background : "none",
+ currentFilter: note && note.filter ? note.filter : "none",
mode: "modal",
});
}
@@ -1843,15 +1873,15 @@ window.setModalNoteColor = function (color) {
}
};
-window.setModalNoteBackground = function (backgroundKey) {
+window.setModalNoteFilter = function (filterKey) {
const modal = document.querySelector(".note-modal-overlay");
if (!modal || !modal.currentNote) return;
const currentNote = modal.currentNote;
- const normalizedBackgroundKey = backgroundKey === "none" ? "none" : normalizeBackgroundKey(backgroundKey) || "none";
- setNoteBackground(currentNote.id, normalizedBackgroundKey, currentNote.editUrl);
+ const normalizedFilterKey = normalizeFilterKey(filterKey) || "none";
+ setNoteFilter(currentNote.id, normalizedFilterKey, currentNote.editUrl);
- currentNote.background = normalizedBackgroundKey;
+ currentNote.filter = normalizedFilterKey;
const modalCard = modal.querySelector(".note-modal");
if (modalCard) {
applyNoteVisualState(modalCard, currentNote);
@@ -1870,7 +1900,7 @@ function generatePaletteButtons(note) {
entityId: note && note.id ? note.id : "",
editUrl: note && note.editUrl ? note.editUrl : "",
currentColor: note && note.color ? note.color : "default",
- currentBackground: note && note.background ? note.background : "none",
+ currentFilter: note && note.filter ? note.filter : "none",
mode: "entity",
});
}
@@ -1879,17 +1909,17 @@ window.setNoteColor = function (noteId, color, editUrl) {
// 1. Visual Update (Immediate feedback)
const card = document.querySelector(`.note-card[data-id="${noteId}"]`);
if (card) {
- const background = card.dataset.background || "none";
- applyNoteVisualState(card, { color, background });
+ const filter = card.dataset.filter || "none";
+ applyNoteVisualState(card, { color, filter });
}
const bookmarkCard = document.querySelector(`.link-outer[data-id="${noteId}"]`);
if (bookmarkCard) {
- const background = bookmarkCard.dataset.background || "none";
- applyNoteVisualState(bookmarkCard, { color, background });
+ const filter = bookmarkCard.dataset.filter || "none";
+ applyNoteVisualState(bookmarkCard, { color, filter });
const palettePopup = bookmarkCard.querySelector(".bookmark-palette .palette-popup");
if (palettePopup) {
- palettePopup.innerHTML = generateBookmarkPaletteButtons(noteId, editUrl, color, background);
+ palettePopup.innerHTML = generateBookmarkPaletteButtons(noteId, editUrl, color, filter);
}
}
@@ -1969,6 +1999,90 @@ window.setNoteColor = function (noteId, color, editUrl) {
});
};
+window.setNoteFilter = function (noteId, filterKey, editUrl) {
+ const normalizedFilterKey = normalizeFilterKey(filterKey) || "none";
+
+ const card = document.querySelector(`.note-card[data-id="${noteId}"]`);
+ if (card) {
+ const colorClass = Array.from(card.classList).find((cls) => cls.startsWith("note-color-"));
+ const color = colorClass ? colorClass.replace("note-color-", "") : card.dataset.color || "default";
+ applyNoteVisualState(card, { color, filter: normalizedFilterKey });
+ }
+
+ const bookmarkCard = document.querySelector(`.link-outer[data-id="${noteId}"]`);
+ if (bookmarkCard) {
+ const colorClass = Array.from(bookmarkCard.classList).find((cls) => cls.startsWith("note-color-"));
+ const color = colorClass ? colorClass.replace("note-color-", "") : bookmarkCard.dataset.color || "default";
+ applyNoteVisualState(bookmarkCard, { color, filter: normalizedFilterKey });
+ const palettePopup = bookmarkCard.querySelector(".bookmark-palette .palette-popup");
+ if (palettePopup) {
+ palettePopup.innerHTML = generateBookmarkPaletteButtons(noteId, editUrl, color, normalizedFilterKey);
+ }
+ }
+
+ const modal = document.querySelector(".note-modal-overlay");
+ if (modal && modal.currentNote && String(modal.currentNote.id) === String(noteId)) {
+ modal.currentNote.filter = normalizedFilterKey;
+ const modalCard = modal.querySelector(".note-modal");
+ if (modalCard) {
+ applyNoteVisualState(modalCard, modal.currentNote);
+ }
+ const modalColorPopup = modal.querySelector("#note-modal-color-popup");
+ if (modalColorPopup) {
+ modalColorPopup.innerHTML = generateModalPaletteButtons(modal.currentNote);
+ }
+ }
+
+ fetch(editUrl)
+ .then((response) => response.text())
+ .then((html) => {
+ const parser = new DOMParser();
+ const doc = parser.parseFromString(html, "text/html");
+ const form = doc.querySelector('form[name="linkform"]');
+
+ if (!form) throw new Error("Could not find edit form");
+
+ const formData = new URLSearchParams();
+ const inputs = form.querySelectorAll("input, textarea");
+
+ inputs.forEach((input) => {
+ if (input.type === "checkbox") {
+ if (input.checked) formData.append(input.name, input.value || "on");
+ } else if (input.name) {
+ formData.append(input.name, input.value);
+ }
+ });
+
+ let currentTags = formData.get("lf_tags") || "";
+ let tagsArray = currentTags.split(/[\s,]+/).filter((t) => t.trim() !== "");
+
+ tagsArray = tagsArray.filter((t) => !t.startsWith(NOTE_FILTER_TAG_PREFIX));
+ if (normalizedFilterKey && normalizedFilterKey !== "none") {
+ tagsArray.push(`${NOTE_FILTER_TAG_PREFIX}${normalizedFilterKey}`);
+ }
+
+ formData.set("lf_tags", tagsArray.join(" "));
+ formData.append("save_edit", "1");
+
+ return fetch(form.action, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ body: formData.toString(),
+ });
+ })
+ .then((response) => {
+ if (!response.ok) {
+ throw new Error("Failed to save filter");
+ }
+ })
+ .catch((err) => {
+ console.error("Error saving note filter:", err);
+ alert("Erreur lors de la sauvegarde du filtre. Veuillez rafraîchir la page.");
+ });
+};
+
window.setNoteBackground = function (noteId, backgroundKey, editUrl) {
const normalizedBackgroundKey = backgroundKey === "none" ? "none" : normalizeBackgroundKey(backgroundKey) || "none";
@@ -1976,18 +2090,16 @@ window.setNoteBackground = function (noteId, backgroundKey, editUrl) {
if (card) {
const colorClass = Array.from(card.classList).find((cls) => cls.startsWith("note-color-"));
const color = colorClass ? colorClass.replace("note-color-", "") : card.dataset.color || "default";
- applyNoteVisualState(card, { color, background: normalizedBackgroundKey });
+ const filter = card.dataset.filter || "none";
+ applyNoteVisualState(card, { color, filter, background: normalizedBackgroundKey });
}
const bookmarkCard = document.querySelector(`.link-outer[data-id="${noteId}"]`);
if (bookmarkCard) {
const colorClass = Array.from(bookmarkCard.classList).find((cls) => cls.startsWith("note-color-"));
const color = colorClass ? colorClass.replace("note-color-", "") : bookmarkCard.dataset.color || "default";
- applyNoteVisualState(bookmarkCard, { color, background: normalizedBackgroundKey });
- const palettePopup = bookmarkCard.querySelector(".bookmark-palette .palette-popup");
- if (palettePopup) {
- palettePopup.innerHTML = generateBookmarkPaletteButtons(noteId, editUrl, color, normalizedBackgroundKey);
- }
+ const filter = bookmarkCard.dataset.filter || "none";
+ applyNoteVisualState(bookmarkCard, { color, filter, background: normalizedBackgroundKey });
}
const modal = document.querySelector(".note-modal-overlay");
@@ -2053,6 +2165,28 @@ window.setNoteBackground = function (noteId, backgroundKey, editUrl) {
});
};
+window.setModalNoteBackground = function (backgroundKey) {
+ const modal = document.querySelector(".note-modal-overlay");
+ if (!modal || !modal.currentNote) return;
+
+ const currentNote = modal.currentNote;
+ const normalizedBackgroundKey = backgroundKey === "none" ? "none" : normalizeBackgroundKey(backgroundKey) || "none";
+ setNoteBackground(currentNote.id, normalizedBackgroundKey, currentNote.editUrl);
+
+ currentNote.background = normalizedBackgroundKey;
+ const modalCard = modal.querySelector(".note-modal");
+ if (modalCard) {
+ applyNoteVisualState(modalCard, currentNote);
+ }
+
+ const modalColorPopup = modal.querySelector("#note-modal-color-popup");
+ if (modalColorPopup) {
+ modalColorPopup.innerHTML = generateModalPaletteButtons(currentNote);
+ modalColorPopup.classList.add("open");
+ positionPalettePopup(modalColorPopup);
+ }
+};
+
function addTagToNote(editUrl, tag) {
return fetch(editUrl)
.then((response) => response.text())