diff --git a/source/components/datatable/save-button.mjs b/source/components/datatable/save-button.mjs
index 4c9bcc1eebdcd29c871b8d832edc609595e0dd26..9d930fc5121851c964a3727bdecf1b748f86975b 100644
--- a/source/components/datatable/save-button.mjs
+++ b/source/components/datatable/save-button.mjs
@@ -99,6 +99,8 @@ class SaveButton extends CustomElement {
 	 * @property {string} classes.badge The badge class
 	 * @property {Array} ignoreChanges The ignore changes (regex)
 	 * @property {Array} data The data
+	 * @property {boolean} disabled The disabled state
+	 * @property {string} logLevel The log level (off, debug)
 	 * @return {Object}
 	 */
 	get defaults() {
@@ -125,6 +127,9 @@ class SaveButton extends CustomElement {
 			data: {},
 
 			disabled: false,
+
+			logLevel: "off",
+
 		});
 
 		updateOptionsFromArguments.call(this, obj);
@@ -199,6 +204,27 @@ class SaveButton extends CustomElement {
 					const ignoreChanges = self.getOption("ignoreChanges");
 
 					const result = diff(self[originValuesSymbol], currentValues);
+					if(self.getOption("logLevel") === "debug") {
+						console.groupCollapsed("SaveButton");
+						console.log(result);
+
+						if(isArray(result)&&result.length>0) {
+							const formattedDiff = result.map(change => ({
+								Operator: change?.operator,
+								Path: change?.path?.join("."),
+								"First Value": change?.first?.value,
+								"First Type": change?.first?.type,
+								"Second Value": change?.second?.value,
+								"Second Type": change?.second?.type
+							}));
+
+							console.table(formattedDiff);
+						} else {
+							console.log("There are no changes to save");
+						}
+						console.groupEnd();
+
+					}
 
 					if (isArray(ignoreChanges) && ignoreChanges.length > 0) {
 						const itemsToRemove = [];
diff --git a/source/dom/updater.mjs b/source/dom/updater.mjs
index 693cf5487e4c4a061d5e435284d72f2ee364f19e..2c1ec37e60cb09d781a34e205598655aa255b9e6 100644
--- a/source/dom/updater.mjs
+++ b/source/dom/updater.mjs
@@ -404,6 +404,16 @@ function retrieveAndSetValue(element) {
 
     const type = element.getAttribute(ATTRIBUTE_UPDATER_BIND_TYPE);
     switch (type) {
+
+        case "integer?":
+        case "int?":
+        case "number?":
+            value = Number(value);
+            if (isNaN(value)||0===value) {
+                value = undefined;
+            }
+            break;
+
         case "number":
         case "int":
         case "float":
diff --git a/source/text/formatter.mjs b/source/text/formatter.mjs
index d521a2182517dddeeb436d4af7fb813d2bb2edb7..85507a557c31b4697322067df6a98426e509d58a 100644
--- a/source/text/formatter.mjs
+++ b/source/text/formatter.mjs
@@ -238,6 +238,8 @@ function format(text) {
 		throw new Error("too deep nesting");
 	}
 
+	validateString(text);
+
 	const openMarker =
 		this[internalSymbol]["marker"]["open"]?.[this[markerOpenIndexSymbol]];
 	const closeMarker =