add multi-page notepad and memos toolbox tool
All checks were successful
Deploy Sokko G / deploy (push) Successful in 16s

This commit is contained in:
Shinuwa 2026-08-25 12:03:11 +02:00
parent fcabbe8029
commit 6125336338
33 changed files with 3787 additions and 138 deletions

View file

@ -8,6 +8,7 @@ import { compactEquipmentPlannerDataForStorage } from "./equipmentPlanner.js";
import { compactGoalTreeDataForStorage } from "./goalTree.js";
import { compactImageAnnotationDataForStorage, compactImagesDataForStorage } from "./imageAnnotation.js";
import { compactLinksDataForStorage } from "./links.js";
import { compactMemosDataForStorage } from "./memos.js";
import { compactNotepadDataForStorage } from "./notepad.js";
import { compactTableDataForStorage } from "./table.js";
import { compactTaskPlannerDataForStorage } from "./taskPlanner.js";
@ -15,6 +16,7 @@ import { compactTimerDataForStorage } from "./timer.js";
export const MODULE_DATA_COMPACTORS = {
notepad: compactNotepadDataForStorage,
memos: compactMemosDataForStorage,
checklist: compactChecklistDataForStorage,
images: compactImagesDataForStorage,
imageAnnotation: compactImageAnnotationDataForStorage,

View file

@ -0,0 +1,43 @@
// Rôle : normalise et compacte les mémos courts des toolboxes.
import { NOTEPAD_ALLOWED_COLORS } from "./drawing.js";
import { normalizeIsoDate, uid } from "./shared.js";
const DEFAULT_MEMO_COLOR = "#f6c453";
function normalizeMemoItem(item) {
const text = String(item?.text || "").trim();
if (!text) return null;
const color = NOTEPAD_ALLOWED_COLORS.has(String(item?.color || "").toLowerCase()) ? String(item.color).toLowerCase() : DEFAULT_MEMO_COLOR;
return {
id: item?.id || uid("memo"),
text,
color,
updatedAt: normalizeIsoDate(item?.updatedAt)
};
}
export function normalizeMemosData(data) {
const source = data && typeof data === "object" ? data : {};
return {
tickerEnabled: source.tickerEnabled !== false,
items: (Array.isArray(source.items) ? source.items : []).map(normalizeMemoItem).filter(Boolean)
};
}
export function compactMemosDataForStorage(value) {
const normalized = normalizeMemosData(value);
const compact = {};
if (normalized.tickerEnabled === false) compact.tickerEnabled = false;
if (normalized.items.length) {
compact.items = normalized.items.map((item) => {
const compactItem = {
id: item.id,
text: item.text,
color: item.color
};
if (item.updatedAt) compactItem.updatedAt = item.updatedAt;
return compactItem;
});
}
return Object.keys(compact).length ? compact : null;
}

View file

@ -1,6 +1,6 @@
// Rôle : normalise, nettoie et compacte les données du bloc-notes riche.
// Rôle : normalise, nettoie et compacte les pages du bloc-note riche.
import { NOTEPAD_ALLOWED_COLORS, normalizeDrawingData } from "./drawing.js";
import { normalizeIsoDate } from "./shared.js";
import { normalizeIsoDate, uid } from "./shared.js";
const NOTEPAD_ALLOWED_TAGS = new Set(["p", "div", "br", "strong", "b", "em", "i", "u", "s", "strike", "ul", "ol", "li", "h3", "h4", "span", "mark"]);
const NOTEPAD_RGB_COLOR_MAP = {
@ -106,24 +106,66 @@ function notepadHtmlToText(value) {
.trim();
}
export function normalizeNotepadData(data) {
const source = data && typeof data === "object" ? data : {};
function normalizeNotepadPage(page, index = 0) {
const source = page && typeof page === "object" ? page : {};
const legacyText = String(source.text || "");
const html = sanitizeNotepadHtml(source.html ? source.html : textToNotepadHtml(legacyText));
const text = notepadHtmlToText(html) || legacyText.trim();
const updatedAt = normalizeIsoDate(source.updatedAt);
const drawingMode = source.drawingMode === "permanent" ? "permanent" : "temporary";
const drawings = normalizeDrawingData(source.drawings);
return { html, text, updatedAt, drawingMode, drawings };
return {
id: source.id || uid("page"),
title: String(source.title || `Page ${index + 1}`).trim() || `Page ${index + 1}`,
html,
text,
updatedAt,
drawingMode,
drawings
};
}
function normalizeLegacyNotepadData(data) {
const source = data && typeof data === "object" ? data : {};
return normalizeNotepadPage({ ...source, id: source.id || uid("page"), title: source.title || "Page 1" }, 0);
}
export function normalizeNotepadData(data) {
const source = data && typeof data === "object" ? data : {};
const pages = (Array.isArray(source.pages) ? source.pages : [normalizeLegacyNotepadData(source)])
.map((page, index) => normalizeNotepadPage(page, index))
.filter(Boolean);
const normalizedPages = pages.length ? pages : [normalizeNotepadPage({ title: "Page 1" }, 0)];
const activePageId = normalizedPages.some((page) => page.id === source.activePageId) ? source.activePageId : normalizedPages[0].id;
const activePage = normalizedPages.find((page) => page.id === activePageId) || normalizedPages[0];
return {
activePageId,
pages: normalizedPages,
html: activePage.html,
text: activePage.text,
updatedAt: activePage.updatedAt,
drawingMode: activePage.drawingMode,
drawings: activePage.drawings
};
}
export function compactNotepadDataForStorage(value) {
const normalized = normalizeNotepadData(value);
const compact = {};
if (normalized.html && normalized.text) compact.html = normalized.html;
if (normalized.text) compact.text = normalized.text;
if (normalized.updatedAt) compact.updatedAt = normalized.updatedAt;
if (normalized.drawingMode === "permanent") compact.drawingMode = "permanent";
if (normalized.drawingMode === "permanent" && normalized.drawings.strokes.length) compact.drawings = normalized.drawings;
return Object.keys(compact).length ? compact : null;
const hasMultiplePages = normalized.pages.length > 1;
const pages = normalized.pages.map((page, index) => {
const compact = {
id: page.id,
title: page.title || `Page ${index + 1}`
};
if (page.html && page.text) compact.html = page.html;
if (page.text) compact.text = page.text;
if (page.updatedAt) compact.updatedAt = page.updatedAt;
if (page.drawingMode === "permanent") compact.drawingMode = "permanent";
if (page.drawingMode === "permanent" && page.drawings.strokes.length) compact.drawings = page.drawings;
return compact;
}).filter((page) => hasMultiplePages || page.text || page.html || page.drawings?.strokes?.length || page.title !== "Page 1");
if (!pages.length) return null;
const compact = { pages };
if (pages.some((page) => page.id === normalized.activePageId) && normalized.activePageId !== pages[0].id) compact.activePageId = normalized.activePageId;
return compact;
}

View file

@ -16,6 +16,8 @@ export const ID_PREFIXES = {
goal: "g",
relation: "e",
stroke: "d",
page: "p",
memo: "w",
equipmentType: "y",
equipment: "q",
trait: "v",

View file

@ -13,6 +13,7 @@ export { normalizeEquipmentPlannerData, summarizeEquipmentPlannerData } from "./
export { normalizeGoalTreeData } from "./modules/goalTree.js";
export { labelFromFileName, normalizeImageAnnotationData } from "./modules/imageAnnotation.js";
export { hostnameFromUrl, normalizeLinksData, normalizeUrl } from "./modules/links.js";
export { normalizeMemosData } from "./modules/memos.js";
export { normalizeNotepadData, sanitizeNotepadHtml } from "./modules/notepad.js";
export { uid } from "./modules/shared.js";
export { normalizeTableData } from "./modules/table.js";
@ -215,6 +216,28 @@ function remapModuleDataForExport(type, data, nextId) {
return { counters: compact.counters.map((counter) => ({ ...counter, id: nextId("counter") })) };
}
if (type === "memos") {
const remapped = {};
if (compact.tickerEnabled === false) remapped.tickerEnabled = false;
if (compact.items) remapped.items = compact.items.map((memo) => ({ ...memo, id: nextId("memo") }));
return remapped;
}
if (type === "notepad") {
const remapped = {
pages: compact.pages.map((page) => ({
...page,
id: nextId("page"),
drawings: page.drawings?.strokes?.length
? { strokes: page.drawings.strokes.map((stroke) => ({ ...stroke, id: nextId("stroke") })) }
: page.drawings
}))
};
const activeIndex = compact.pages.findIndex((page) => page.id === compact.activePageId);
if (activeIndex > 0) remapped.activePageId = remapped.pages[activeIndex].id;
return remapped;
}
if (type === "combos") {
const remapped = {};
if (compact.device) remapped.device = compact.device;