improve notepad features and add drawing canvas
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s
This commit is contained in:
parent
e73d754082
commit
de1b8a5638
20 changed files with 1732 additions and 141 deletions
|
|
@ -87,6 +87,31 @@ export function validateSiteContent(site) {
|
|||
"toolboxes.storageHelp.title",
|
||||
"toolboxes.storageHelp.text",
|
||||
"toolboxes.modules.notepad.placeholder",
|
||||
"toolboxes.modules.notepad.toolbarLabel",
|
||||
"toolboxes.modules.notepad.textFormatLabel",
|
||||
"toolboxes.modules.notepad.boldTitle",
|
||||
"toolboxes.modules.notepad.italicTitle",
|
||||
"toolboxes.modules.notepad.underlineTitle",
|
||||
"toolboxes.modules.notepad.paragraphTitle",
|
||||
"toolboxes.modules.notepad.headingTitle",
|
||||
"toolboxes.modules.notepad.bulletListTitle",
|
||||
"toolboxes.modules.notepad.numberListTitle",
|
||||
"toolboxes.modules.notepad.colorGroupLabel",
|
||||
"toolboxes.modules.notepad.textColorLabel",
|
||||
"toolboxes.modules.notepad.highlightColorLabel",
|
||||
"toolboxes.modules.notepad.drawingLabel",
|
||||
"toolboxes.modules.notepad.drawTitle",
|
||||
"toolboxes.modules.notepad.eraseTitle",
|
||||
"toolboxes.modules.notepad.temporaryTitle",
|
||||
"toolboxes.modules.notepad.permanentTitle",
|
||||
"toolboxes.modules.notepad.undoDrawingTitle",
|
||||
"toolboxes.modules.notepad.clearDrawingTitle",
|
||||
"toolboxes.modules.notepad.drawingColorLabel",
|
||||
"toolboxes.modules.notepad.drawingWidthLabel",
|
||||
"toolboxes.modules.notepad.temporaryStatus",
|
||||
"toolboxes.modules.notepad.permanentStatus",
|
||||
"toolboxes.modules.notepad.updatedAtPrefix",
|
||||
"toolboxes.modules.notepad.sessionStorageError",
|
||||
"toolboxes.modules.checklist.itemPlaceholder",
|
||||
"toolboxes.modules.checklist.sectionPlaceholder",
|
||||
"toolboxes.modules.checklist.quantityLabel",
|
||||
|
|
@ -246,6 +271,16 @@ export function validateSiteContent(site) {
|
|||
"toolboxes.modules.imageAnnotation.emptyMarkers",
|
||||
"toolboxes.modules.imageAnnotation.deleteImageTitle",
|
||||
"toolboxes.modules.imageAnnotation.deleteMarkerTitle",
|
||||
"toolboxes.modules.imageAnnotation.drawingLabel",
|
||||
"toolboxes.modules.imageAnnotation.drawTitle",
|
||||
"toolboxes.modules.imageAnnotation.eraseTitle",
|
||||
"toolboxes.modules.imageAnnotation.undoDrawingTitle",
|
||||
"toolboxes.modules.imageAnnotation.clearDrawingTitle",
|
||||
"toolboxes.modules.imageAnnotation.drawingColorLabel",
|
||||
"toolboxes.modules.imageAnnotation.drawingWidthLabel",
|
||||
"toolboxes.modules.imageAnnotation.temporaryTitle",
|
||||
"toolboxes.modules.imageAnnotation.permanentTitle",
|
||||
"toolboxes.modules.imageAnnotation.sessionStorageError",
|
||||
"toolboxes.emptyTitle",
|
||||
"toolboxes.emptyText"
|
||||
].forEach((path) => assertNonEmptyString(site, path));
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(moduleRegistry, /module-content/);
|
||||
assert.match(moduleRegistry, /module-scroll-button/);
|
||||
assert.match(moduleRegistry, /Icon name="dropdown"/);
|
||||
assert.match(moduleRegistry, /aria-expanded=\{quickOpen\}/);
|
||||
assert.match(moduleRegistry, /CompactDropdown/);
|
||||
assert.doesNotMatch(source, /<select value="" onChange=\{\(event\) => addModule/);
|
||||
assert.match(notepadModule, /export function NotepadModule/);
|
||||
assert.match(calculatorModule, /export function CalculatorModule/);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { test } from "node:test";
|
|||
import assert from "node:assert/strict";
|
||||
import { parseColonImportLines } from "../website/src/features/toolboxes/modules/textImport.js";
|
||||
import { getTimePatternRecurrenceMs, getTimePatternTargetMs } from "../website/src/features/toolboxes/modules/timerUtils.js";
|
||||
import { compactModuleDataForStorage, createToolboxExportPayload, normalizeTaskPlannerData } from "../website/src/features/toolboxes/storage/toolboxStorage.js";
|
||||
import { compactModuleDataForStorage, createToolboxExportPayload, normalizeNotepadData, normalizeTaskPlannerData } from "../website/src/features/toolboxes/storage/toolboxStorage.js";
|
||||
|
||||
test("colon text import keeps urls intact after the first separator", () => {
|
||||
assert.deepEqual(parseColonImportLines("Build:https://example.com/build?class=rogue\nPotion:10"), [
|
||||
|
|
@ -28,6 +28,82 @@ test("time pattern recurrence uses configured frequency instead of next remainin
|
|||
assert.equal(getTimePatternRecurrenceMs("X:X:30"), 60 * 1000);
|
||||
});
|
||||
|
||||
test("notepad data migrates text, sanitizes html and compacts drawings", () => {
|
||||
const migrated = normalizeNotepadData({ text: "Boss\nPhase 2" });
|
||||
assert.equal(migrated.text, "Boss\nPhase 2");
|
||||
assert.match(migrated.html, /<p>Boss<\/p><p>Phase 2<\/p>/);
|
||||
|
||||
const normalized = normalizeNotepadData({
|
||||
html: '<h3 onclick="bad()">Titre</h3><script>alert(1)</script><span style="color: rgb(246, 196, 83); position: fixed">Important</span><img src=x>',
|
||||
updatedAt: "2026-07-29T12:00:00.000Z",
|
||||
drawingMode: "permanent",
|
||||
drawings: {
|
||||
strokes: [
|
||||
{
|
||||
id: "stroke1",
|
||||
color: "#f6c453",
|
||||
width: 40,
|
||||
points: [{ x: 1.123 }, { x: 4, y: 8 }, { x: 12.345, y: 16.789 }]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
assert.match(normalized.html, /<h3>Titre<\/h3>/);
|
||||
assert.match(normalized.html, /<span style="color: #f6c453">Important<\/span>/);
|
||||
assert.doesNotMatch(normalized.html, /script|onclick|position|img/);
|
||||
assert.equal(normalized.updatedAt, "2026-07-29T12:00:00.000Z");
|
||||
assert.equal(normalized.drawings.strokes[0].width, 24);
|
||||
assert.deepEqual(normalized.drawings.strokes[0].points, [{ x: 4, y: 8 }, { x: 12.35, y: 16.79 }]);
|
||||
|
||||
const compactTemporary = compactModuleDataForStorage("notepad", {
|
||||
html: "<p>Note</p>",
|
||||
text: "Note",
|
||||
drawingMode: "temporary",
|
||||
drawings: { strokes: normalized.drawings.strokes }
|
||||
});
|
||||
assert.equal(compactTemporary.drawings, undefined);
|
||||
assert.equal(compactTemporary.drawingMode, undefined);
|
||||
|
||||
const compactPermanent = compactModuleDataForStorage("notepad", {
|
||||
html: "<p>Note</p>",
|
||||
text: "Note",
|
||||
updatedAt: "not a date",
|
||||
drawingMode: "permanent",
|
||||
drawings: normalized.drawings
|
||||
});
|
||||
assert.equal(compactPermanent.updatedAt, undefined);
|
||||
assert.equal(compactPermanent.drawingMode, "permanent");
|
||||
assert.equal(compactPermanent.drawings.strokes.length, 1);
|
||||
});
|
||||
|
||||
test("image annotation storage keeps drawing strokes as percent coordinates", () => {
|
||||
const compact = compactModuleDataForStorage("imageAnnotation", {
|
||||
image: "data:image/png;base64,aaa",
|
||||
markers: [{ id: "marker1", x: 120, y: 50, label: "Boss" }],
|
||||
drawings: {
|
||||
strokes: [
|
||||
{
|
||||
id: "stroke1",
|
||||
color: "#22d3ee",
|
||||
width: 4,
|
||||
points: [{ x: 10.123, y: 20.456 }, { x: 140, y: -4 }]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(compact.markers[0].x, 100);
|
||||
assert.deepEqual(compact.drawings.strokes[0].points, [{ x: 10.12, y: 20.46 }, { x: 100, y: 0 }]);
|
||||
|
||||
const exported = createToolboxExportPayload(
|
||||
{ id: "toolbox1", name: "Map", modules: [{ id: "module1", type: "imageAnnotation" }], updatedAt: "2026-01-01T00:00:00.000Z" },
|
||||
{ "toolbox1:module1": compact }
|
||||
);
|
||||
assert.equal(exported.modules.m1.markers[0].id, "k1");
|
||||
assert.equal(exported.modules.m1.drawings.strokes[0].id, "d1");
|
||||
});
|
||||
|
||||
test("task planner data normalizes invalid settings and relations", () => {
|
||||
const normalized = normalizeTaskPlannerData({
|
||||
weeklyResetDay: 9,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue