From 6f6393fd6d6f3e49efd8a4db575a1b5444c0f4ec Mon Sep 17 00:00:00 2001 From: David-Alexandre Chanel Date: Fri, 2 Oct 2026 16:28:38 +0200 Subject: [PATCH 01/15] Add local visual preset helpers --- src/visual-presets.js | 98 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/visual-presets.js diff --git a/src/visual-presets.js b/src/visual-presets.js new file mode 100644 index 0000000..7ffb1a3 --- /dev/null +++ b/src/visual-presets.js @@ -0,0 +1,98 @@ +const USER_PRESET_PREFIX = 'user:'; +const MAX_PRESET_NAME_LENGTH = 48; + +export function normalizeUserPresets(value) { + if (!Array.isArray(value)) return []; + + const result = []; + const seenIds = new Set(); + + for (const item of value) { + if (!item || typeof item !== 'object') continue; + + const label = normalizePresetName(item.label); + const options = isPlainObject(item.options) ? { ...item.options } : undefined; + if (!label || !options) continue; + + let id = typeof item.id === 'string' && item.id.startsWith(USER_PRESET_PREFIX) + ? item.id + : createPresetId(label, result); + + if (seenIds.has(id)) id = createPresetId(label, result); + seenIds.add(id); + + result.push({ id, label, options }); + } + + return result; +} + +export function upsertUserPreset(presets, label, options) { + const cleanLabel = normalizePresetName(label); + if (!cleanLabel || !isPlainObject(options)) return undefined; + + const current = normalizeUserPresets(presets); + const existingIndex = current.findIndex( + (preset) => preset.label.toLocaleLowerCase() === cleanLabel.toLocaleLowerCase() + ); + + const preset = existingIndex >= 0 + ? { ...current[existingIndex], label: cleanLabel, options: { ...options } } + : { + id: createPresetId(cleanLabel, current), + label: cleanLabel, + options: { ...options } + }; + + if (existingIndex >= 0) current.splice(existingIndex, 1, preset); + else current.push(preset); + + current.sort((left, right) => left.label.localeCompare(right.label)); + return { presets: current, preset }; +} + +export function removeUserPreset(presets, id) { + if (!isUserPresetId(id)) return normalizeUserPresets(presets); + return normalizeUserPresets(presets).filter((preset) => preset.id !== id); +} + +export function getUserPreset(presets, id) { + if (!isUserPresetId(id)) return undefined; + return normalizeUserPresets(presets).find((preset) => preset.id === id); +} + +export function isUserPresetId(id) { + return typeof id === 'string' && id.startsWith(USER_PRESET_PREFIX); +} + +export function normalizePresetName(value) { + return String(value ?? '') + .trim() + .replace(/\s+/g, ' ') + .slice(0, MAX_PRESET_NAME_LENGTH); +} + +function createPresetId(label, presets) { + const slug = label + .toLocaleLowerCase() + .normalize('NFKD') + .replace(/[\u0300-\u036f]/g, '') + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .slice(0, 32) || 'preset'; + + const used = new Set(presets.map((preset) => preset.id)); + let index = 1; + let id = `${USER_PRESET_PREFIX}${slug}`; + + while (used.has(id)) { + index += 1; + id = `${USER_PRESET_PREFIX}${slug}-${index}`; + } + + return id; +} + +function isPlainObject(value) { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} From 3391a5617a9f4d94316e96b25f1c9829b36c9f66 Mon Sep 17 00:00:00 2001 From: David-Alexandre Chanel Date: Fri, 2 Oct 2026 16:29:02 +0200 Subject: [PATCH 02/15] Add local preset actions and prioritize Augmenta data --- index.html | 101 ++++++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/index.html b/index.html index 557b7d4..ebd832d 100644 --- a/index.html +++ b/index.html @@ -5,9 +5,9 @@ - - - + + + Augmenta Web Demo @@ -127,6 +127,48 @@

Augmenta Web Demo

+
+ + + + Augmenta data + + No data yet + + +
+
+
+ + +
+ +
+ +
+
+ + + +
+
+ + +
+
+ +
+ +
+ +
+
Connect to Augmenta or run the demo to inspect the stream.
+
+
+
+
@@ -138,11 +180,18 @@

Augmenta Web Demo

- +
+ +
+ + +
+
+

Saved presets stay local to this browser.

-
- - - - Augmenta data - - No data yet - - -
-
-
- - -
- -
- -
-
- - - -
-
- - -
-
- -
- -
- -
-
Connect to Augmenta or run the demo to inspect the stream.
-
-
-
-