feat: refactorer le système de palette de couleurs avec popup unifié, simplification du CSS pour les notes colorées et backgrounds, et amélioration du positionnement responsive de la palette
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
window.SHAARLI_NOTE_BACKGROUNDS_MANIFEST = [
|
||||
{
|
||||
key: "cafe",
|
||||
label: "Café",
|
||||
files: {
|
||||
light: "bg-light-cafe.jpg",
|
||||
dark: "bg-dark-cafe.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "codes",
|
||||
label: "Codes",
|
||||
files: {
|
||||
light: "bg-light-codes.jpg",
|
||||
dark: "bg-dark-codes.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "dune",
|
||||
label: "Dune",
|
||||
files: {
|
||||
light: "bg-light-dune.jpg",
|
||||
dark: "bg-dark-dune.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "feuille-ligne",
|
||||
label: "Feuille Ligne",
|
||||
files: {
|
||||
light: "bg-light-feuilleLigne.jpg",
|
||||
dark: "bg-dark-feuilleLigne.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "feuille-quadrillage",
|
||||
label: "Feuille Quadrillage",
|
||||
files: {
|
||||
light: "bg-light-feuilleQuadrille.jpg",
|
||||
dark: "bg-dark-feuilleQuadrille.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "fleurs",
|
||||
label: "Fleurs",
|
||||
files: {
|
||||
light: "bg-light-fleurs.jpg",
|
||||
dark: "bg-dark-fleurs.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "foret",
|
||||
label: "Forêt",
|
||||
files: {
|
||||
light: "bg-light-foret.jpg",
|
||||
dark: "bg-dark-foret.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "grid",
|
||||
label: "Grid",
|
||||
files: {
|
||||
light: "bg-light-grid.jpg",
|
||||
dark: "bg-dark-grid.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "journal",
|
||||
label: "Journal",
|
||||
files: {
|
||||
light: "bg-light-journal.jpg",
|
||||
dark: "bg-dark-journal.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "lecture",
|
||||
label: "Lecture",
|
||||
files: {
|
||||
light: "bg-light-lecture.jpg",
|
||||
dark: "bg-dark-lecture.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "legumes",
|
||||
label: "Légumes",
|
||||
files: {
|
||||
light: "bg-light-legumes.jpg",
|
||||
dark: "bg-dark-legumes.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "montagnes",
|
||||
label: "Montagnes",
|
||||
files: {
|
||||
light: "bg-light-montagnes.jpg",
|
||||
dark: "bg-dark-montagnes.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "ocean",
|
||||
label: "Océan",
|
||||
files: {
|
||||
light: "bg-light-ocean.jpg",
|
||||
dark: "bg-dark-ocean.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "sports",
|
||||
label: "Sports",
|
||||
files: {
|
||||
light: "bg-light-sports.jpg",
|
||||
dark: "bg-dark-sports.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "vague1",
|
||||
label: "Vague 1",
|
||||
files: {
|
||||
light: "bg-light-vague1.jpg",
|
||||
dark: "bg-dark-vague1.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "vague2",
|
||||
label: "Vague 2",
|
||||
files: {
|
||||
light: "bg-light-vague2.jpg",
|
||||
dark: "bg-dark-vague2.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "ville",
|
||||
label: "Ville",
|
||||
files: {
|
||||
light: "bg-light-ville.jpg",
|
||||
dark: "bg-dark-ville.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "voyage",
|
||||
label: "Voyage",
|
||||
files: {
|
||||
light: "bg-light-voyage.jpg",
|
||||
dark: "bg-dark-voyage.jpg",
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,146 @@
|
||||
[
|
||||
{
|
||||
"key": "cafe",
|
||||
"label": "Café",
|
||||
"files": {
|
||||
"light": "bg-light-cafe.jpg",
|
||||
"dark": "bg-dark-cafe.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "codes",
|
||||
"label": "Codes",
|
||||
"files": {
|
||||
"light": "bg-light-codes.jpg",
|
||||
"dark": "bg-dark-codes.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "dune",
|
||||
"label": "Dune",
|
||||
"files": {
|
||||
"light": "bg-light-dune.jpg",
|
||||
"dark": "bg-dark-dune.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "feuille-ligne",
|
||||
"label": "Feuille Ligne",
|
||||
"files": {
|
||||
"light": "bg-light-feuilleLigne.jpg",
|
||||
"dark": "bg-dark-feuilleLigne.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "feuille-quadrillage",
|
||||
"label": "Feuille Quadrillage",
|
||||
"files": {
|
||||
"light": "bg-light-feuilleQuadrille.jpg",
|
||||
"dark": "bg-dark-feuilleQuadrille.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "fleurs",
|
||||
"label": "Fleurs",
|
||||
"files": {
|
||||
"light": "bg-light-fleurs.jpg",
|
||||
"dark": "bg-dark-fleurs.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "foret",
|
||||
"label": "Forêt",
|
||||
"files": {
|
||||
"light": "bg-light-foret.jpg",
|
||||
"dark": "bg-dark-foret.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "grid",
|
||||
"label": "Grid",
|
||||
"files": {
|
||||
"light": "bg-light-grid.jpg",
|
||||
"dark": "bg-dark-grid.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "journal",
|
||||
"label": "Journal",
|
||||
"files": {
|
||||
"light": "bg-light-journal.jpg",
|
||||
"dark": "bg-dark-journal.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "lecture",
|
||||
"label": "Lecture",
|
||||
"files": {
|
||||
"light": "bg-light-lecture.jpg",
|
||||
"dark": "bg-dark-lecture.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "legumes",
|
||||
"label": "Légumes",
|
||||
"files": {
|
||||
"light": "bg-light-legumes.jpg",
|
||||
"dark": "bg-dark-legumes.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "montagnes",
|
||||
"label": "Montagnes",
|
||||
"files": {
|
||||
"light": "bg-light-montagnes.jpg",
|
||||
"dark": "bg-dark-montagnes.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "ocean",
|
||||
"label": "Océan",
|
||||
"files": {
|
||||
"light": "bg-light-ocean.jpg",
|
||||
"dark": "bg-dark-ocean.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "sports",
|
||||
"label": "Sports",
|
||||
"files": {
|
||||
"light": "bg-light-sports.jpg",
|
||||
"dark": "bg-dark-sports.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "vague1",
|
||||
"label": "Vague 1",
|
||||
"files": {
|
||||
"light": "bg-light-vague1.jpg",
|
||||
"dark": "bg-dark-vague1.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "vague2",
|
||||
"label": "Vague 2",
|
||||
"files": {
|
||||
"light": "bg-light-vague2.jpg",
|
||||
"dark": "bg-dark-vague2.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "ville",
|
||||
"label": "Ville",
|
||||
"files": {
|
||||
"light": "bg-light-ville.jpg",
|
||||
"dark": "bg-dark-ville.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "voyage",
|
||||
"label": "Voyage",
|
||||
"files": {
|
||||
"light": "bg-light-voyage.jpg",
|
||||
"dark": "bg-dark-voyage.jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
+469
-66
@@ -6,38 +6,117 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
const linkList = document.getElementById("links-list");
|
||||
const container = document.querySelector(".content-container");
|
||||
|
||||
if (typeof initBookmarkPaletteButtons === "function") {
|
||||
initBookmarkPaletteButtons();
|
||||
}
|
||||
const startViewInitialization = function () {
|
||||
if (typeof initBookmarkPaletteButtons === "function") {
|
||||
initBookmarkPaletteButtons();
|
||||
}
|
||||
|
||||
// Always init Pinned Items logic (sorting and listeners)
|
||||
// This function is defined at the end of the file
|
||||
if (typeof initPinnedItems === "function") {
|
||||
initPinnedItems();
|
||||
}
|
||||
// Always init Pinned Items logic (sorting and listeners)
|
||||
// This function is defined at the end of the file
|
||||
if (typeof initPinnedItems === "function") {
|
||||
initPinnedItems();
|
||||
}
|
||||
|
||||
if (!linkList || !container) return;
|
||||
if (!linkList || !container) return;
|
||||
|
||||
if (searchTags === "todo") {
|
||||
initTodoView(linkList, container);
|
||||
} else if (searchTags === "note") {
|
||||
initNoteView(linkList, container);
|
||||
if (searchTags === "todo") {
|
||||
initTodoView(linkList, container);
|
||||
} else if (searchTags === "note") {
|
||||
initNoteView(linkList, container);
|
||||
}
|
||||
};
|
||||
|
||||
// Charger les backgrounds dynamiquement, puis initialiser les vues
|
||||
initThemeModeBackgroundSync();
|
||||
|
||||
if (typeof loadBackgroundOptions === "function") {
|
||||
loadBackgroundOptions()
|
||||
.then(() => {
|
||||
startViewInitialization();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Erreur lors du chargement des backgrounds:", err);
|
||||
// Initialiser quand même même si le chargement échoue
|
||||
startViewInitialization();
|
||||
});
|
||||
} else {
|
||||
startViewInitialization();
|
||||
}
|
||||
});
|
||||
|
||||
const NOTE_COLOR_OPTIONS = [
|
||||
{ key: "default", label: "Par défaut", hex: "#20293A" },
|
||||
{ key: "red", label: "Rouge", hex: "#f28b82" },
|
||||
{ key: "orange", label: "Orange", hex: "#fbbc04" },
|
||||
{ key: "yellow", label: "Jaune", hex: "#fff475" },
|
||||
{ key: "green", label: "Vert", hex: "#ccff90" },
|
||||
{ key: "teal", label: "Menthe", hex: "#a7ffeb" },
|
||||
{ key: "blue", label: "Bleu clair", hex: "#cbf0f8" },
|
||||
{ key: "darkblue", label: "Bleu", hex: "#aecbfa" },
|
||||
{ key: "purple", label: "Violet", hex: "#d7aefb" },
|
||||
{ key: "pink", label: "Rose", hex: "#fdcfe8" },
|
||||
{ key: "brown", label: "Beige", hex: "#e6c9a8" },
|
||||
{ key: "grey", label: "Gris", hex: "#e8eaed" },
|
||||
{
|
||||
key: "default",
|
||||
label: "Par défaut",
|
||||
light: "#ffffff",
|
||||
dark: "#20293A"
|
||||
},
|
||||
{
|
||||
key: "red",
|
||||
label: "Rouge",
|
||||
light: "#f28b82",
|
||||
dark: "#9c2116"
|
||||
},
|
||||
{
|
||||
key: "orange",
|
||||
label: "Orange",
|
||||
light: "#fbbc04",
|
||||
dark: "#9c7a16"
|
||||
},
|
||||
{
|
||||
key: "yellow",
|
||||
label: "Jaune",
|
||||
light: "#fff475",
|
||||
dark: "#9c9116"
|
||||
},
|
||||
{
|
||||
key: "green",
|
||||
label: "Vert",
|
||||
light: "#ccff90",
|
||||
dark: "#5e9c16"
|
||||
},
|
||||
{
|
||||
key: "teal",
|
||||
label: "Menthe",
|
||||
light: "#a7ffeb",
|
||||
dark: "#169c7d"
|
||||
},
|
||||
{
|
||||
key: "blue",
|
||||
label: "Bleu clair",
|
||||
light: "#cbf0f8",
|
||||
dark: "#16849c"
|
||||
},
|
||||
{
|
||||
key: "darkblue",
|
||||
label: "Bleu",
|
||||
light: "#aecbfa",
|
||||
dark: "#16499c"
|
||||
},
|
||||
{
|
||||
key: "purple",
|
||||
label: "Violet",
|
||||
light: "#d7aefb",
|
||||
dark: "#5d169c"
|
||||
},
|
||||
{
|
||||
key: "pink",
|
||||
label: "Rose",
|
||||
light: "#fdcfe8",
|
||||
dark: "#9c165f"
|
||||
},
|
||||
{
|
||||
key: "brown",
|
||||
label: "Beige",
|
||||
light: "#e6c9a8",
|
||||
dark: "#9c5d16"
|
||||
},
|
||||
{
|
||||
key: "grey",
|
||||
label: "Gris",
|
||||
light: "#e8eaed",
|
||||
dark: "#4e5764"
|
||||
}
|
||||
];
|
||||
|
||||
const NOTE_BACKGROUND_TAG_PREFIX = "notebg-";
|
||||
@@ -68,25 +147,325 @@ function resolveThemeAssetBasePath() {
|
||||
return "/tpl/shaarli-pro";
|
||||
}
|
||||
|
||||
const NOTE_BACKGROUND_BASE_PATH = `${resolveThemeAssetBasePath().replace(/\/$/, "")}/img/note-backgrounds`;
|
||||
const NOTE_BACKGROUND_OPTIONS = [
|
||||
{ key: "bg-canyon", label: "Canyon", file: "bg-canyon.png" },
|
||||
{ key: "bg-leaves", label: "Feuilles", file: "bg-leaves.png" },
|
||||
{ key: "bg-shore", label: "Rivage", file: "bg-shore.png" },
|
||||
{ key: "bg-sunset", label: "Coucher", file: "bg-sunset.png" },
|
||||
{ key: "bg-planet", label: "Planète", file: "bg-planet.png" },
|
||||
{ key: "bg-crystal", label: "Cristal", file: "bg-crystal.png" },
|
||||
{ key: "bg-orchid", label: "Orchidée", file: "bg-orchid.png" },
|
||||
{ key: "bg-lake", label: "Lac", file: "bg-lake.png" },
|
||||
{ key: "bg-ladder", label: "Échelle", file: "bg-ladder.png" },
|
||||
{ key: "bg-burst", label: "Étoile", file: "bg-burst.png" },
|
||||
const NOTE_BACKGROUND_ASSET_ROOT = `${resolveThemeAssetBasePath().replace(/\/$/, "")}/img`;
|
||||
|
||||
// Liste des backgrounds - chargée dynamiquement depuis le serveur
|
||||
let NOTE_BACKGROUND_OPTIONS = [];
|
||||
let isBackgroundOptionsLoaded = false;
|
||||
let backgroundOptionsLoadPromise = null;
|
||||
|
||||
window.__shaarliCustomViewsVersion = "1.0.5-inline-bg-manifest";
|
||||
if (typeof console !== "undefined" && console && typeof console.info === "function") {
|
||||
console.info("[custom_views] loaded", window.__shaarliCustomViewsVersion);
|
||||
}
|
||||
|
||||
const NOTE_BACKGROUND_MANIFEST_INLINE = [
|
||||
{ key: "cafe", label: "Café", files: { light: "bg-light-cafe.jpg", dark: "bg-dark-cafe.jpg" } },
|
||||
{ key: "codes", label: "Codes", files: { light: "bg-light-codes.jpg", dark: "bg-dark-codes.jpg" } },
|
||||
{ key: "dune", label: "Dune", files: { light: "bg-light-dune.jpg", dark: "bg-dark-dune.jpg" } },
|
||||
{ key: "feuille-ligne", label: "Feuille Ligne", files: { light: "bg-light-feuilleLigne.jpg", dark: "bg-dark-feuilleLigne.jpg" } },
|
||||
{ key: "feuille-quadrillage", label: "Feuille Quadrillage", files: { light: "bg-light-feuilleQuadrille.jpg", dark: "bg-dark-feuilleQuadrille.jpg" } },
|
||||
{ key: "fleurs", label: "Fleurs", files: { light: "bg-light-fleurs.jpg", dark: "bg-dark-fleurs.jpg" } },
|
||||
{ key: "foret", label: "Forêt", files: { light: "bg-light-foret.jpg", dark: "bg-dark-foret.jpg" } },
|
||||
{ key: "grid", label: "Grid", files: { light: "bg-light-grid.jpg", dark: "bg-dark-grid.jpg" } },
|
||||
{ key: "journal", label: "Journal", files: { light: "bg-light-journal.jpg", dark: "bg-dark-journal.jpg" } },
|
||||
{ key: "lecture", label: "Lecture", files: { light: "bg-light-lecture.jpg", dark: "bg-dark-lecture.jpg" } },
|
||||
{ key: "legumes", label: "Légumes", files: { light: "bg-light-legumes.jpg", dark: "bg-dark-legumes.jpg" } },
|
||||
{ key: "montagnes", label: "Montagnes", files: { light: "bg-light-montagnes.jpg", dark: "bg-dark-montagnes.jpg" } },
|
||||
{ key: "ocean", label: "Océan", files: { light: "bg-light-ocean.jpg", dark: "bg-dark-ocean.jpg" } },
|
||||
{ key: "sports", label: "Sports", files: { light: "bg-light-sports.jpg", dark: "bg-dark-sports.jpg" } },
|
||||
{ key: "vague1", label: "Vague 1", files: { light: "bg-light-vague1.jpg", dark: "bg-dark-vague1.jpg" } },
|
||||
{ key: "vague2", label: "Vague 2", files: { light: "bg-light-vague2.jpg", dark: "bg-dark-vague2.jpg" } },
|
||||
{ key: "ville", label: "Ville", files: { light: "bg-light-ville.jpg", dark: "bg-dark-ville.jpg" } },
|
||||
{ key: "voyage", label: "Voyage", files: { light: "bg-light-voyage.jpg", dark: "bg-dark-voyage.jpg" } },
|
||||
];
|
||||
|
||||
function getNoteBackgroundUrl(backgroundKey) {
|
||||
const found = NOTE_BACKGROUND_OPTIONS.find((bg) => bg.key === backgroundKey);
|
||||
if (!found) return "";
|
||||
/**
|
||||
* Charge la liste des backgrounds depuis le endpoint PHP
|
||||
* @returns {Promise<Array>} Liste des backgrounds
|
||||
*/
|
||||
function loadBackgroundOptions() {
|
||||
if (isBackgroundOptionsLoaded) {
|
||||
return Promise.resolve(NOTE_BACKGROUND_OPTIONS);
|
||||
}
|
||||
|
||||
return `${NOTE_BACKGROUND_BASE_PATH}/${found.file}`;
|
||||
if (backgroundOptionsLoadPromise) {
|
||||
return backgroundOptionsLoadPromise;
|
||||
}
|
||||
|
||||
NOTE_BACKGROUND_OPTIONS = normalizeDynamicBackgroundOptions(NOTE_BACKGROUND_MANIFEST_INLINE);
|
||||
isBackgroundOptionsLoaded = true;
|
||||
backgroundOptionsLoadPromise = Promise.resolve(NOTE_BACKGROUND_OPTIONS);
|
||||
return backgroundOptionsLoadPromise;
|
||||
}
|
||||
|
||||
function normalizeBackgroundKey(key) {
|
||||
if (typeof key !== "string") return "";
|
||||
|
||||
return key
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[\s_]+/g, "-")
|
||||
.replace(/[^a-z0-9-]/g, "-")
|
||||
.replace(/-+/g, "-")
|
||||
.replace(/^-|-$/g, "");
|
||||
}
|
||||
|
||||
function formatBackgroundLabel(rawLabel, fallbackKey) {
|
||||
const base = (typeof rawLabel === "string" && rawLabel.trim() !== "" ? rawLabel : fallbackKey)
|
||||
.replace(/[_-]+/g, " ")
|
||||
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
|
||||
return base
|
||||
.split(" ")
|
||||
.filter((part) => part !== "")
|
||||
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function getCurrentThemeMode() {
|
||||
return document.documentElement.getAttribute("data-theme") === "dark" ? "dark" : "light";
|
||||
}
|
||||
|
||||
function getColorOption(colorKey) {
|
||||
const normalizedKey = typeof colorKey === "string" ? colorKey.trim().toLowerCase() : "";
|
||||
|
||||
return NOTE_COLOR_OPTIONS.find((opt) => opt.key === normalizedKey) || NOTE_COLOR_OPTIONS.find((opt) => opt.key === "default") || null;
|
||||
}
|
||||
|
||||
function getThemeColorValue(option, mode = getCurrentThemeMode()) {
|
||||
if (!option || typeof option !== "object") return "";
|
||||
|
||||
const safeMode = mode === "dark" ? "dark" : "light";
|
||||
const preferred = typeof option[safeMode] === "string" ? option[safeMode].trim() : "";
|
||||
const fallbackLight = typeof option.light === "string" ? option.light.trim() : "";
|
||||
const fallbackDark = typeof option.dark === "string" ? option.dark.trim() : "";
|
||||
|
||||
return preferred || fallbackLight || fallbackDark;
|
||||
}
|
||||
|
||||
function parseHexColor(colorValue) {
|
||||
if (typeof colorValue !== "string") return null;
|
||||
|
||||
const safeValue = colorValue.trim();
|
||||
const shortHexMatch = /^#([0-9a-f]{3})$/i.exec(safeValue);
|
||||
if (shortHexMatch) {
|
||||
return {
|
||||
r: parseInt(shortHexMatch[1].charAt(0).repeat(2), 16),
|
||||
g: parseInt(shortHexMatch[1].charAt(1).repeat(2), 16),
|
||||
b: parseInt(shortHexMatch[1].charAt(2).repeat(2), 16),
|
||||
};
|
||||
}
|
||||
|
||||
const longHexMatch = /^#([0-9a-f]{6})$/i.exec(safeValue);
|
||||
if (longHexMatch) {
|
||||
return {
|
||||
r: parseInt(longHexMatch[1].slice(0, 2), 16),
|
||||
g: parseInt(longHexMatch[1].slice(2, 4), 16),
|
||||
b: parseInt(longHexMatch[1].slice(4, 6), 16),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getReadableForegroundForBackground(colorValue, mode = getCurrentThemeMode()) {
|
||||
const rgb = parseHexColor(colorValue);
|
||||
if (!rgb) {
|
||||
return mode === "dark" ? "#e8eaed" : "#202124";
|
||||
}
|
||||
|
||||
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
|
||||
return luminance > 0.56 ? "#202124" : "#f5f7fb";
|
||||
}
|
||||
|
||||
function normalizeDynamicBackgroundOptions(rawOptions) {
|
||||
const rawList = Array.isArray(rawOptions)
|
||||
? rawOptions
|
||||
: rawOptions && Array.isArray(rawOptions.backgrounds)
|
||||
? rawOptions.backgrounds
|
||||
: [];
|
||||
|
||||
const mergedByKey = {};
|
||||
|
||||
rawList.forEach((option) => {
|
||||
if (!option || typeof option !== "object") return;
|
||||
|
||||
const key = normalizeBackgroundKey(typeof option.key === "string" ? option.key : option.label || "");
|
||||
if (!key) return;
|
||||
|
||||
const files = option.files && typeof option.files === "object" ? option.files : {};
|
||||
const lightFile = typeof files.light === "string" ? files.light.trim() : "";
|
||||
const darkFile = typeof files.dark === "string" ? files.dark.trim() : "";
|
||||
if (!lightFile && !darkFile) return;
|
||||
|
||||
if (!mergedByKey[key]) {
|
||||
mergedByKey[key] = {
|
||||
key,
|
||||
label: formatBackgroundLabel(option.label || "", key),
|
||||
paths: {
|
||||
light: "",
|
||||
dark: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (lightFile) {
|
||||
mergedByKey[key].paths.light = `note-bg-light/${lightFile}`;
|
||||
}
|
||||
if (darkFile) {
|
||||
mergedByKey[key].paths.dark = `note-bg-dark/${darkFile}`;
|
||||
}
|
||||
});
|
||||
|
||||
return Object.values(mergedByKey).sort((a, b) => a.label.localeCompare(b.label, "fr", { sensitivity: "base" }));
|
||||
}
|
||||
|
||||
function getAvailableBackgroundOptionsForMode(mode = getCurrentThemeMode()) {
|
||||
const safeMode = mode === "dark" ? "dark" : "light";
|
||||
return NOTE_BACKGROUND_OPTIONS.filter((option) => option.paths && option.paths[safeMode]);
|
||||
}
|
||||
|
||||
function getNoteBackgroundUrl(backgroundKey, mode = getCurrentThemeMode()) {
|
||||
const normalizedKey = normalizeBackgroundKey(backgroundKey);
|
||||
if (!normalizedKey) return "";
|
||||
|
||||
const found = NOTE_BACKGROUND_OPTIONS.find((bg) => bg.key === normalizedKey);
|
||||
if (!found || !found.paths) return "";
|
||||
|
||||
const safeMode = mode === "dark" ? "dark" : "light";
|
||||
const selectedPath = found.paths[safeMode] || "";
|
||||
|
||||
if (!selectedPath) return "";
|
||||
return `${NOTE_BACKGROUND_ASSET_ROOT}/${selectedPath}`;
|
||||
}
|
||||
|
||||
function getElementVisualColor(element) {
|
||||
if (!element) return "default";
|
||||
|
||||
const colorClass = Array.from(element.classList).find((cls) => cls.startsWith("note-color-"));
|
||||
if (colorClass) {
|
||||
return colorClass.replace("note-color-", "") || "default";
|
||||
}
|
||||
|
||||
return element.dataset.color || "default";
|
||||
}
|
||||
|
||||
function getElementVisualBackground(element) {
|
||||
if (!element) return "none";
|
||||
|
||||
const normalizedBackground = normalizeBackgroundKey(element.dataset.background || "");
|
||||
return normalizedBackground || "none";
|
||||
}
|
||||
|
||||
function refreshNoteBackgroundVisuals() {
|
||||
document.querySelectorAll(".note-card, .note-modal, .link-outer").forEach((element) => {
|
||||
applyNoteVisualState(element, {
|
||||
color: getElementVisualColor(element),
|
||||
background: getElementVisualBackground(element),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function refreshBackgroundPalettes() {
|
||||
document.querySelectorAll(".note-card").forEach((card) => {
|
||||
const palettePopup = card.querySelector(".note-hover-actions .palette-popup");
|
||||
if (!palettePopup) return;
|
||||
|
||||
const noteId = card.dataset.id || "";
|
||||
if (!noteId) return;
|
||||
|
||||
const editLink = card.querySelector(
|
||||
'.note-hover-actions a[href*="/admin/shaare/"], .note-hover-actions a[href*="do=editlink"], .note-hover-actions a[title="Edit"]',
|
||||
);
|
||||
|
||||
palettePopup.innerHTML = generatePaletteButtons({
|
||||
id: noteId,
|
||||
editUrl: editLink && editLink.href ? editLink.href : "#",
|
||||
color: getElementVisualColor(card),
|
||||
background: getElementVisualBackground(card),
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll(".link-outer").forEach((card) => {
|
||||
const palettePopup = card.querySelector(".bookmark-palette .palette-popup");
|
||||
if (!palettePopup) return;
|
||||
|
||||
const noteId = card.dataset.id || card.getAttribute("data-id") || card.id;
|
||||
if (!noteId) return;
|
||||
|
||||
const actions = card.querySelector(".link-actions");
|
||||
const editLink = actions
|
||||
? actions.querySelector('a[title="Modifier"], a[aria-label="Modifier ce bookmark"], a[href*="/admin/shaare/"]')
|
||||
: null;
|
||||
|
||||
palettePopup.innerHTML = generateBookmarkPaletteButtons(
|
||||
noteId,
|
||||
editLink && editLink.href ? editLink.href : "#",
|
||||
getElementVisualColor(card),
|
||||
getElementVisualBackground(card),
|
||||
);
|
||||
});
|
||||
|
||||
const modal = document.querySelector(".note-modal-overlay");
|
||||
if (!modal || !modal.currentNote) return;
|
||||
|
||||
const modalCard = modal.querySelector(".note-modal");
|
||||
if (modalCard) {
|
||||
modal.currentNote.color = getElementVisualColor(modalCard);
|
||||
modal.currentNote.background = getElementVisualBackground(modalCard);
|
||||
}
|
||||
|
||||
const modalColorPopup = modal.querySelector("#note-modal-color-popup");
|
||||
if (modalColorPopup) {
|
||||
modalColorPopup.innerHTML = generateModalPaletteButtons(modal.currentNote);
|
||||
}
|
||||
}
|
||||
|
||||
function parseBackgroundManifestPayload(rawPayload) {
|
||||
const payload = typeof rawPayload === "string" ? rawPayload.trim() : "";
|
||||
if (!payload) return [];
|
||||
|
||||
try {
|
||||
return JSON.parse(payload);
|
||||
} catch (parseError) {
|
||||
const firstArrayBracket = payload.indexOf("[");
|
||||
const lastArrayBracket = payload.lastIndexOf("]");
|
||||
if (firstArrayBracket === -1 || lastArrayBracket === -1 || lastArrayBracket <= firstArrayBracket) {
|
||||
throw parseError;
|
||||
}
|
||||
|
||||
const jsonSlice = payload.slice(firstArrayBracket, lastArrayBracket + 1);
|
||||
return JSON.parse(jsonSlice);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let isThemeModeBackgroundSyncInitialized = false;
|
||||
|
||||
function initThemeModeBackgroundSync() {
|
||||
if (isThemeModeBackgroundSyncInitialized) return;
|
||||
|
||||
const root = document.documentElement;
|
||||
if (!root) return;
|
||||
|
||||
let lastTheme = getCurrentThemeMode();
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
const themeChanged = mutations.some((mutation) => mutation.attributeName === "data-theme");
|
||||
if (!themeChanged) return;
|
||||
|
||||
const nextTheme = getCurrentThemeMode();
|
||||
if (nextTheme === lastTheme) return;
|
||||
|
||||
lastTheme = nextTheme;
|
||||
refreshNoteBackgroundVisuals();
|
||||
refreshBackgroundPalettes();
|
||||
});
|
||||
|
||||
observer.observe(root, { attributes: true, attributeFilter: ["data-theme"] });
|
||||
isThemeModeBackgroundSyncInitialized = true;
|
||||
}
|
||||
|
||||
function positionPalettePopup(popup) {
|
||||
@@ -123,14 +502,29 @@ function positionPalettePopup(popup) {
|
||||
function applyNoteVisualState(element, note) {
|
||||
if (!element || !note) return;
|
||||
|
||||
const color = note.color || "default";
|
||||
const background = note.background || "none";
|
||||
const resolvedColorOption = getColorOption(note.color || "default");
|
||||
const color = resolvedColorOption ? resolvedColorOption.key : "default";
|
||||
const colorValue = getThemeColorValue(resolvedColorOption);
|
||||
const foregroundColor = getReadableForegroundForBackground(colorValue);
|
||||
const normalizedBackground = normalizeBackgroundKey(note.background || "");
|
||||
const background = normalizedBackground || "none";
|
||||
|
||||
element.classList.forEach((cls) => {
|
||||
if (cls.startsWith("note-color-")) element.classList.remove(cls);
|
||||
});
|
||||
element.classList.add(`note-color-${color}`);
|
||||
|
||||
if (colorValue) {
|
||||
element.style.backgroundColor = colorValue;
|
||||
element.style.borderColor = "transparent";
|
||||
} else {
|
||||
element.style.removeProperty("background-color");
|
||||
}
|
||||
|
||||
if (foregroundColor) {
|
||||
element.style.setProperty("--note-card-fg", foregroundColor);
|
||||
}
|
||||
|
||||
if (background && background !== "none") {
|
||||
const bgUrl = getNoteBackgroundUrl(background);
|
||||
if (bgUrl) {
|
||||
@@ -166,8 +560,8 @@ function extractNoteVisualStateFromTags(tags) {
|
||||
let background = "none";
|
||||
const foundBgTag = safeTags.find((t) => typeof t === "string" && t.startsWith(NOTE_BACKGROUND_TAG_PREFIX));
|
||||
if (foundBgTag) {
|
||||
const candidate = foundBgTag.substring(NOTE_BACKGROUND_TAG_PREFIX.length);
|
||||
if (candidate && NOTE_BACKGROUND_OPTIONS.some((bg) => bg.key === candidate)) {
|
||||
const candidate = normalizeBackgroundKey(foundBgTag.substring(NOTE_BACKGROUND_TAG_PREFIX.length));
|
||||
if (candidate) {
|
||||
background = candidate;
|
||||
}
|
||||
}
|
||||
@@ -246,19 +640,21 @@ function initBookmarkPaletteButtons() {
|
||||
|
||||
function generateBookmarkPaletteButtons(bookmarkId, editUrl, currentColor, currentBackground) {
|
||||
const color = currentColor || "default";
|
||||
const background = currentBackground || "none";
|
||||
const background = normalizeBackgroundKey(currentBackground || "") || "none";
|
||||
|
||||
const colorButtons = [
|
||||
`<button class="palette-btn palette-btn-default ${color === "default" ? "is-active" : ""}" title="Par défaut" onclick="setNoteColor('${bookmarkId}', 'default', '${editUrl}')"><i class="mdi mdi-format-color-reset"></i></button>`,
|
||||
...NOTE_COLOR_OPTIONS.filter((opt) => opt.key !== "default").map(
|
||||
(opt) =>
|
||||
`<button class="palette-btn note-color-${opt.key} ${color === opt.key ? "is-active" : ""}" title="${opt.label}" onclick="setNoteColor('${bookmarkId}', '${opt.key}', '${editUrl}')" style="background-color:${opt.hex}"></button>`,
|
||||
(opt) => {
|
||||
const swatchColor = getThemeColorValue(opt);
|
||||
return `<button class="palette-btn note-color-${opt.key} ${color === opt.key ? "is-active" : ""}" title="${opt.label}" onclick="setNoteColor('${bookmarkId}', '${opt.key}', '${editUrl}')" style="background-color:${swatchColor}"></button>`;
|
||||
},
|
||||
),
|
||||
].join("");
|
||||
|
||||
const backgroundButtons = [
|
||||
`<button class="palette-btn palette-btn-bg-none ${background === "none" ? "is-active" : ""}" title="Sans image" onclick="setNoteBackground('${bookmarkId}', 'none', '${editUrl}')"><i class="mdi mdi-image-off-outline"></i></button>`,
|
||||
...NOTE_BACKGROUND_OPTIONS.map((bg) => {
|
||||
...getAvailableBackgroundOptionsForMode().map((bg) => {
|
||||
const bgUrl = getNoteBackgroundUrl(bg.key);
|
||||
return `<button class="palette-btn palette-btn-bg ${background === bg.key ? "is-active" : ""}" title="${bg.label}" onclick="setNoteBackground('${bookmarkId}', '${bg.key}', '${editUrl}')" style="background-image:url('${bgUrl}')"></button>`;
|
||||
}),
|
||||
@@ -754,7 +1150,7 @@ function parseNoteFromLink(linkEl) {
|
||||
tags.push(t);
|
||||
}
|
||||
} else if (t.startsWith(NOTE_BACKGROUND_TAG_PREFIX)) {
|
||||
background = t.substring(NOTE_BACKGROUND_TAG_PREFIX.length) || "none";
|
||||
background = normalizeBackgroundKey(t.substring(NOTE_BACKGROUND_TAG_PREFIX.length)) || "none";
|
||||
} else {
|
||||
tags.push(t);
|
||||
}
|
||||
@@ -970,19 +1366,21 @@ function openNoteModal(note) {
|
||||
|
||||
function generateModalPaletteButtons(note) {
|
||||
const currentColor = note.color || "default";
|
||||
const currentBackground = note.background || "none";
|
||||
const currentBackground = normalizeBackgroundKey(note.background || "") || "none";
|
||||
|
||||
const colorButtons = [
|
||||
`<button class="palette-btn palette-btn-default ${currentColor === "default" ? "is-active" : ""}" title="Par défaut" onclick="setModalNoteColor('default')"><i class="mdi mdi-format-color-reset"></i></button>`,
|
||||
...NOTE_COLOR_OPTIONS.filter((opt) => opt.key !== "default").map(
|
||||
(opt) =>
|
||||
`<button class="palette-btn note-color-${opt.key} ${currentColor === opt.key ? "is-active" : ""}" title="${opt.label}" onclick="setModalNoteColor('${opt.key}')" style="background-color:${opt.hex}"></button>`,
|
||||
(opt) => {
|
||||
const swatchColor = getThemeColorValue(opt);
|
||||
return `<button class="palette-btn note-color-${opt.key} ${currentColor === opt.key ? "is-active" : ""}" title="${opt.label}" onclick="setModalNoteColor('${opt.key}')" style="background-color:${swatchColor}"></button>`;
|
||||
},
|
||||
),
|
||||
].join("");
|
||||
|
||||
const backgroundButtons = [
|
||||
`<button class="palette-btn palette-btn-bg-none ${currentBackground === "none" ? "is-active" : ""}" title="Sans image" onclick="setModalNoteBackground('none')"><i class="mdi mdi-image-off-outline"></i></button>`,
|
||||
...NOTE_BACKGROUND_OPTIONS.map((bg) => {
|
||||
...getAvailableBackgroundOptionsForMode().map((bg) => {
|
||||
const bgUrl = getNoteBackgroundUrl(bg.key);
|
||||
return `<button class="palette-btn palette-btn-bg ${currentBackground === bg.key ? "is-active" : ""}" title="${bg.label}" onclick="setModalNoteBackground('${bg.key}')" style="background-image:url('${bgUrl}')"></button>`;
|
||||
}),
|
||||
@@ -1020,9 +1418,10 @@ window.setModalNoteBackground = function (backgroundKey) {
|
||||
if (!modal || !modal.currentNote) return;
|
||||
|
||||
const currentNote = modal.currentNote;
|
||||
setNoteBackground(currentNote.id, backgroundKey, currentNote.editUrl);
|
||||
const normalizedBackgroundKey = backgroundKey === "none" ? "none" : normalizeBackgroundKey(backgroundKey) || "none";
|
||||
setNoteBackground(currentNote.id, normalizedBackgroundKey, currentNote.editUrl);
|
||||
|
||||
currentNote.background = backgroundKey;
|
||||
currentNote.background = normalizedBackgroundKey;
|
||||
const modalCard = modal.querySelector(".note-modal");
|
||||
if (modalCard) {
|
||||
applyNoteVisualState(modalCard, currentNote);
|
||||
@@ -1038,19 +1437,21 @@ window.setModalNoteBackground = function (backgroundKey) {
|
||||
|
||||
function generatePaletteButtons(note) {
|
||||
const currentColor = note.color || "default";
|
||||
const currentBackground = note.background || "none";
|
||||
const currentBackground = normalizeBackgroundKey(note.background || "") || "none";
|
||||
|
||||
const colorButtons = [
|
||||
`<button class="palette-btn palette-btn-default ${currentColor === "default" ? "is-active" : ""}" title="Par défaut" onclick="setNoteColor('${note.id}', 'default', '${note.editUrl}')"><i class="mdi mdi-format-color-reset"></i></button>`,
|
||||
...NOTE_COLOR_OPTIONS.filter((opt) => opt.key !== "default").map(
|
||||
(opt) =>
|
||||
`<button class="palette-btn note-color-${opt.key} ${currentColor === opt.key ? "is-active" : ""}" title="${opt.label}" onclick="setNoteColor('${note.id}', '${opt.key}', '${note.editUrl}')" style="background-color:${opt.hex}"></button>`,
|
||||
(opt) => {
|
||||
const swatchColor = getThemeColorValue(opt);
|
||||
return `<button class="palette-btn note-color-${opt.key} ${currentColor === opt.key ? "is-active" : ""}" title="${opt.label}" onclick="setNoteColor('${note.id}', '${opt.key}', '${note.editUrl}')" style="background-color:${swatchColor}"></button>`;
|
||||
},
|
||||
),
|
||||
].join("");
|
||||
|
||||
const backgroundButtons = [
|
||||
`<button class="palette-btn palette-btn-bg-none ${currentBackground === "none" ? "is-active" : ""}" title="Sans image" onclick="setNoteBackground('${note.id}', 'none', '${note.editUrl}')"><i class="mdi mdi-image-off-outline"></i></button>`,
|
||||
...NOTE_BACKGROUND_OPTIONS.map((bg) => {
|
||||
...getAvailableBackgroundOptionsForMode().map((bg) => {
|
||||
const bgUrl = getNoteBackgroundUrl(bg.key);
|
||||
return `<button class="palette-btn palette-btn-bg ${currentBackground === bg.key ? "is-active" : ""}" title="${bg.label}" onclick="setNoteBackground('${note.id}', '${bg.key}', '${note.editUrl}')" style="background-image:url('${bgUrl}')"></button>`;
|
||||
}),
|
||||
@@ -1157,27 +1558,29 @@ window.setNoteColor = function (noteId, color, editUrl) {
|
||||
};
|
||||
|
||||
window.setNoteBackground = function (noteId, backgroundKey, editUrl) {
|
||||
const normalizedBackgroundKey = backgroundKey === "none" ? "none" : normalizeBackgroundKey(backgroundKey) || "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, background: backgroundKey });
|
||||
applyNoteVisualState(card, { color, 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: backgroundKey });
|
||||
applyNoteVisualState(bookmarkCard, { color, background: normalizedBackgroundKey });
|
||||
const palettePopup = bookmarkCard.querySelector(".bookmark-palette .palette-popup");
|
||||
if (palettePopup) {
|
||||
palettePopup.innerHTML = generateBookmarkPaletteButtons(noteId, editUrl, color, backgroundKey);
|
||||
palettePopup.innerHTML = generateBookmarkPaletteButtons(noteId, editUrl, color, normalizedBackgroundKey);
|
||||
}
|
||||
}
|
||||
|
||||
const modal = document.querySelector(".note-modal-overlay");
|
||||
if (modal && modal.currentNote && String(modal.currentNote.id) === String(noteId)) {
|
||||
modal.currentNote.background = backgroundKey;
|
||||
modal.currentNote.background = normalizedBackgroundKey;
|
||||
const modalCard = modal.querySelector(".note-modal");
|
||||
if (modalCard) {
|
||||
applyNoteVisualState(modalCard, modal.currentNote);
|
||||
@@ -1212,8 +1615,8 @@ window.setNoteBackground = function (noteId, backgroundKey, editUrl) {
|
||||
let tagsArray = currentTags.split(/[\s,]+/).filter((t) => t.trim() !== "");
|
||||
|
||||
tagsArray = tagsArray.filter((t) => !t.startsWith(NOTE_BACKGROUND_TAG_PREFIX));
|
||||
if (backgroundKey && backgroundKey !== "none") {
|
||||
tagsArray.push(`${NOTE_BACKGROUND_TAG_PREFIX}${backgroundKey}`);
|
||||
if (normalizedBackgroundKey && normalizedBackgroundKey !== "none") {
|
||||
tagsArray.push(`${NOTE_BACKGROUND_TAG_PREFIX}${normalizedBackgroundKey}`);
|
||||
}
|
||||
|
||||
formData.set("lf_tags", tagsArray.join(" "));
|
||||
|
||||
Reference in New Issue
Block a user