Loading editor/html/js/properties.js +27 −3 Changes for editor/html/js/properties.js: 27 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -9,13 +9,18 @@ var PropertiesPanel = (function() { var currentSchema = null; var saveTimer = null; var isSaving = false; var dirty = false; // The properties panel is docked in #sidebar-right; rect (formerly used to // position a floating panel near a canvas click) is no longer needed but // still accepted so callers (Canvas selection) don't need special-casing. function showProperties(uuid) { // Persist unsaved changes before switching to another element. if (currentUuid && uuid && currentUuid !== uuid) { // Persist unsaved changes before switching to another element — but only // if a field actually changed. Saving unconditionally here used to trigger // Preview.refresh() (a full iframe reload) on every selection change, even // when nothing was edited, which reset scroll position and re-ran every // widget's init script. if (currentUuid && uuid && currentUuid !== uuid && dirty) { saveProperties(); } Loading Loading @@ -795,6 +800,23 @@ var PropertiesPanel = (function() { input.name = field.key; input.value = value; if (field.placeholder) input.placeholder = field.placeholder; // Plain text fields can carry preset suggestions (e.g. heading // size presets in em) via a datalist — the input stays free-text, // presets are just quick picks shown by the browser's autocomplete. if (field.options) { var listId = 'preset-' + field.key; var datalist = document.createElement('datalist'); datalist.id = listId; for (var pi = 0; pi < field.options.length; pi++) { var opt = field.options[pi]; var option = document.createElement('option'); option.value = (opt && opt.value !== undefined) ? opt.value : opt; if (opt && opt.label) option.label = opt.label; datalist.appendChild(option); } input.setAttribute('list', listId); group.appendChild(datalist); } } group.appendChild(input); Loading Loading @@ -893,6 +915,7 @@ var PropertiesPanel = (function() { clearTimeout(saveTimer); saveTimer = null; } dirty = false; var form = document.getElementById('prop-form'); if (!form) return; Loading Loading @@ -938,6 +961,7 @@ var PropertiesPanel = (function() { function scheduleSave() { if (!currentUuid) return; dirty = true; if (saveTimer) clearTimeout(saveTimer); saveTimer = setTimeout(function() { saveTimer = null; Loading @@ -946,7 +970,7 @@ var PropertiesPanel = (function() { } function clear() { if (currentUuid) { if (currentUuid && dirty) { saveProperties(); } if (saveTimer) { Loading editor/widgets/grid/grid.cpp +4 −2 Changes for editor/widgets/grid/grid.cpp: 4 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -254,7 +254,7 @@ extern "C" { addField("border_radius", "Border Radius", "text", "e.g. 8px", "desktop"); addField("border_style", "Border", "text", "e.g. 1px solid #ccc", "desktop"); addField("opacity", "Opacity", "text", "0.0 - 1.0", "desktop"); addField("vertical_align", "Vertical Align", "select", nullptr, "desktop", mkOpts({"top","center","bottom"})); addField("vertical_align", "Vertical Align", "select", nullptr, "desktop", mkOpts({"top","center","bottom","stretch"})); addField("hidden", "Hidden", "checkbox", nullptr, "desktop"); { json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1); Loading @@ -270,7 +270,7 @@ extern "C" { addField("m_border_radius", "Mobile Border Radius", "text", "e.g. 8px", "mobile"); addField("m_border_style", "Mobile Border Style", "text", "e.g. 1px solid #ccc", "mobile"); addField("m_opacity", "Mobile Opacity", "text", "0.0 - 1.0", "mobile"); addField("m_vertical_align", "Mobile Vertical Align", "select", nullptr, "mobile", mkOpts({"top","center","bottom"})); addField("m_vertical_align", "Mobile Vertical Align", "select", nullptr, "mobile", mkOpts({"top","center","bottom","stretch"})); addField("m_hidden", "Mobile Hidden", "checkbox", nullptr, "mobile"); { json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1); Loading Loading @@ -599,6 +599,7 @@ extern "C" { if (verticalAlign == "top") style += " align-items: start;"; else if (verticalAlign == "center") style += " align-items: center;"; else if (verticalAlign == "bottom") style += " align-items: end;"; else if (verticalAlign == "stretch") style += " align-items: stretch;"; } if (hidden) { style += " display: none;"; Loading Loading @@ -632,6 +633,7 @@ extern "C" { if (m_verticalAlign == "top") mobileCSS += " align-items: start !important;"; else if (m_verticalAlign == "center") mobileCSS += " align-items: center !important;"; else if (m_verticalAlign == "bottom") mobileCSS += " align-items: end !important;"; else if (m_verticalAlign == "stretch") mobileCSS += " align-items: stretch !important;"; } if (m_hidden) mobileCSS += " display: none !important;"; else if (hidden) mobileCSS += " display: grid !important;"; Loading editor/widgets/textbox/textbox.cpp +27 −2 Changes for editor/widgets/textbox/textbox.cpp: 27 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <cstring> #include <map> #include <string> #include <utility> #include <vector> #include <tinyxml2.h> Loading Loading @@ -283,6 +284,20 @@ extern "C" { for (auto v : vals) json_object_array_add(a, json_object_new_string(v)); return a; }; // Preset suggestions (value + label) for plain text fields, rendered // as a <datalist> so the field stays free-text — e.g. heading-size // presets in em, mirroring the px thresholds llms::headingLevelFor() // (src/llms_text.h) uses to infer H1/H2/H3 semantics from font size. auto mkPresets = [](std::initializer_list<std::pair<const char*, const char*>> vals) { json_object *a = json_object_new_array(); for (auto &v : vals) { json_object *o = json_object_new_object(); json_object_object_add(o, "value", json_object_new_string(v.first)); json_object_object_add(o, "label", json_object_new_string(v.second)); json_object_array_add(a, o); } return a; }; addField("content", "Content", "richtext"); addField("width", "Width", "text", "e.g. 100%", "desktop"); addField("height", "Height", "text", "e.g. auto", "desktop"); Loading @@ -295,7 +310,12 @@ extern "C" { addField("letter_spacing", "Buchstabenabstand", "text", "z.B. 0.05em", "desktop"); addField("word_spacing", "Wortabstand", "text", "z.B. 0.1em", "desktop"); addField("font_family", "Font Family", "text", "e.g. Arial, sans-serif"); addField("font_size", "Font Size", "text", "e.g. 16px", "desktop"); addField("font_size", "Font Size", "text", "e.g. 16px", "desktop", mkPresets({ {"2em", "H1 – 32px"}, {"1.5em", "H2 – 24px"}, {"1.125em", "H3 (fett) – 18px"}, {"1em", "Standard – 16px"} })); addField("font_color", "Font Color (Dark)", "color_var", nullptr, "desktop"); addField("l_font_color", "Font Color (Light)", "color_var", nullptr, "light"); addField("ml_font_color", "Font Color (Mobile Light)", "color_var", nullptr, "mobile_light"); Loading Loading @@ -323,7 +343,12 @@ extern "C" { // Mobile overrides addField("m_width", "Width", "text", "e.g. 100%", "mobile"); addField("m_height", "Height", "text", "e.g. auto", "mobile"); addField("m_font_size", "Font Size", "text", "e.g. 14px", "mobile"); addField("m_font_size", "Font Size", "text", "e.g. 14px", "mobile", mkPresets({ {"1.75em", "H1 – 28px"}, {"1.375em", "H2 – 22px"}, {"1.0625em", "H3 (fett) – 17px"}, {"1em", "Standard – 16px"} })); addField("m_line_height", "Zeilenabstand", "text", "z.B. 1.5", "mobile"); addField("m_letter_spacing", "Buchstabenabstand", "text", "z.B. 0.05em", "mobile"); addField("m_font_color", "Font Color", "color_var", nullptr, "mobile"); Loading Loading
editor/html/js/properties.js +27 −3 Changes for editor/html/js/properties.js: 27 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -9,13 +9,18 @@ var PropertiesPanel = (function() { var currentSchema = null; var saveTimer = null; var isSaving = false; var dirty = false; // The properties panel is docked in #sidebar-right; rect (formerly used to // position a floating panel near a canvas click) is no longer needed but // still accepted so callers (Canvas selection) don't need special-casing. function showProperties(uuid) { // Persist unsaved changes before switching to another element. if (currentUuid && uuid && currentUuid !== uuid) { // Persist unsaved changes before switching to another element — but only // if a field actually changed. Saving unconditionally here used to trigger // Preview.refresh() (a full iframe reload) on every selection change, even // when nothing was edited, which reset scroll position and re-ran every // widget's init script. if (currentUuid && uuid && currentUuid !== uuid && dirty) { saveProperties(); } Loading Loading @@ -795,6 +800,23 @@ var PropertiesPanel = (function() { input.name = field.key; input.value = value; if (field.placeholder) input.placeholder = field.placeholder; // Plain text fields can carry preset suggestions (e.g. heading // size presets in em) via a datalist — the input stays free-text, // presets are just quick picks shown by the browser's autocomplete. if (field.options) { var listId = 'preset-' + field.key; var datalist = document.createElement('datalist'); datalist.id = listId; for (var pi = 0; pi < field.options.length; pi++) { var opt = field.options[pi]; var option = document.createElement('option'); option.value = (opt && opt.value !== undefined) ? opt.value : opt; if (opt && opt.label) option.label = opt.label; datalist.appendChild(option); } input.setAttribute('list', listId); group.appendChild(datalist); } } group.appendChild(input); Loading Loading @@ -893,6 +915,7 @@ var PropertiesPanel = (function() { clearTimeout(saveTimer); saveTimer = null; } dirty = false; var form = document.getElementById('prop-form'); if (!form) return; Loading Loading @@ -938,6 +961,7 @@ var PropertiesPanel = (function() { function scheduleSave() { if (!currentUuid) return; dirty = true; if (saveTimer) clearTimeout(saveTimer); saveTimer = setTimeout(function() { saveTimer = null; Loading @@ -946,7 +970,7 @@ var PropertiesPanel = (function() { } function clear() { if (currentUuid) { if (currentUuid && dirty) { saveProperties(); } if (saveTimer) { Loading
editor/widgets/grid/grid.cpp +4 −2 Changes for editor/widgets/grid/grid.cpp: 4 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -254,7 +254,7 @@ extern "C" { addField("border_radius", "Border Radius", "text", "e.g. 8px", "desktop"); addField("border_style", "Border", "text", "e.g. 1px solid #ccc", "desktop"); addField("opacity", "Opacity", "text", "0.0 - 1.0", "desktop"); addField("vertical_align", "Vertical Align", "select", nullptr, "desktop", mkOpts({"top","center","bottom"})); addField("vertical_align", "Vertical Align", "select", nullptr, "desktop", mkOpts({"top","center","bottom","stretch"})); addField("hidden", "Hidden", "checkbox", nullptr, "desktop"); { json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1); Loading @@ -270,7 +270,7 @@ extern "C" { addField("m_border_radius", "Mobile Border Radius", "text", "e.g. 8px", "mobile"); addField("m_border_style", "Mobile Border Style", "text", "e.g. 1px solid #ccc", "mobile"); addField("m_opacity", "Mobile Opacity", "text", "0.0 - 1.0", "mobile"); addField("m_vertical_align", "Mobile Vertical Align", "select", nullptr, "mobile", mkOpts({"top","center","bottom"})); addField("m_vertical_align", "Mobile Vertical Align", "select", nullptr, "mobile", mkOpts({"top","center","bottom","stretch"})); addField("m_hidden", "Mobile Hidden", "checkbox", nullptr, "mobile"); { json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1); Loading Loading @@ -599,6 +599,7 @@ extern "C" { if (verticalAlign == "top") style += " align-items: start;"; else if (verticalAlign == "center") style += " align-items: center;"; else if (verticalAlign == "bottom") style += " align-items: end;"; else if (verticalAlign == "stretch") style += " align-items: stretch;"; } if (hidden) { style += " display: none;"; Loading Loading @@ -632,6 +633,7 @@ extern "C" { if (m_verticalAlign == "top") mobileCSS += " align-items: start !important;"; else if (m_verticalAlign == "center") mobileCSS += " align-items: center !important;"; else if (m_verticalAlign == "bottom") mobileCSS += " align-items: end !important;"; else if (m_verticalAlign == "stretch") mobileCSS += " align-items: stretch !important;"; } if (m_hidden) mobileCSS += " display: none !important;"; else if (hidden) mobileCSS += " display: grid !important;"; Loading
editor/widgets/textbox/textbox.cpp +27 −2 Changes for editor/widgets/textbox/textbox.cpp: 27 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <cstring> #include <map> #include <string> #include <utility> #include <vector> #include <tinyxml2.h> Loading Loading @@ -283,6 +284,20 @@ extern "C" { for (auto v : vals) json_object_array_add(a, json_object_new_string(v)); return a; }; // Preset suggestions (value + label) for plain text fields, rendered // as a <datalist> so the field stays free-text — e.g. heading-size // presets in em, mirroring the px thresholds llms::headingLevelFor() // (src/llms_text.h) uses to infer H1/H2/H3 semantics from font size. auto mkPresets = [](std::initializer_list<std::pair<const char*, const char*>> vals) { json_object *a = json_object_new_array(); for (auto &v : vals) { json_object *o = json_object_new_object(); json_object_object_add(o, "value", json_object_new_string(v.first)); json_object_object_add(o, "label", json_object_new_string(v.second)); json_object_array_add(a, o); } return a; }; addField("content", "Content", "richtext"); addField("width", "Width", "text", "e.g. 100%", "desktop"); addField("height", "Height", "text", "e.g. auto", "desktop"); Loading @@ -295,7 +310,12 @@ extern "C" { addField("letter_spacing", "Buchstabenabstand", "text", "z.B. 0.05em", "desktop"); addField("word_spacing", "Wortabstand", "text", "z.B. 0.1em", "desktop"); addField("font_family", "Font Family", "text", "e.g. Arial, sans-serif"); addField("font_size", "Font Size", "text", "e.g. 16px", "desktop"); addField("font_size", "Font Size", "text", "e.g. 16px", "desktop", mkPresets({ {"2em", "H1 – 32px"}, {"1.5em", "H2 – 24px"}, {"1.125em", "H3 (fett) – 18px"}, {"1em", "Standard – 16px"} })); addField("font_color", "Font Color (Dark)", "color_var", nullptr, "desktop"); addField("l_font_color", "Font Color (Light)", "color_var", nullptr, "light"); addField("ml_font_color", "Font Color (Mobile Light)", "color_var", nullptr, "mobile_light"); Loading Loading @@ -323,7 +343,12 @@ extern "C" { // Mobile overrides addField("m_width", "Width", "text", "e.g. 100%", "mobile"); addField("m_height", "Height", "text", "e.g. auto", "mobile"); addField("m_font_size", "Font Size", "text", "e.g. 14px", "mobile"); addField("m_font_size", "Font Size", "text", "e.g. 14px", "mobile", mkPresets({ {"1.75em", "H1 – 28px"}, {"1.375em", "H2 – 22px"}, {"1.0625em", "H3 (fett) – 17px"}, {"1em", "Standard – 16px"} })); addField("m_line_height", "Zeilenabstand", "text", "z.B. 1.5", "mobile"); addField("m_letter_spacing", "Buchstabenabstand", "text", "z.B. 0.05em", "mobile"); addField("m_font_color", "Font Color", "color_var", nullptr, "mobile"); Loading