From 8d2748ed1262ad5d516ac9993daa1dfe1768c288 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Sun, 29 Oct 2023 02:31:02 +0200 Subject: [PATCH 01/13] ASE: parameter: value_from_text(): yield integers for text inputs of choices Signed-off-by: Tim Janik --- ase/parameter.cc | 47 +++++++++++++++++++++++++++-------------------- ase/parameter.hh | 1 + 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/ase/parameter.cc b/ase/parameter.cc index 58595171..d70a454d 100644 --- a/ase/parameter.cc +++ b/ase/parameter.cc @@ -159,6 +159,26 @@ Parameter::rescale (double t) const return value; } +size_t +Parameter::match_choice (const ChoiceS &choices, const String &text) +{ + for (size_t i = 0; i < choices.size(); i++) + if (text == choices[i].ident) + return i; + size_t selected = 0; + const String ltext = string_tolower (text); + float best = F32MAX; + for (size_t i = 0; i < choices.size(); i++) { + const size_t maxdist = std::max (choices[i].ident.size(), ltext.size()); + const float dist = damerau_levenshtein_restricted (string_tolower (choices[i].ident), ltext) / maxdist; + if (dist < best) { + best = dist; + selected = i; + } + } + return selected; +} + Value Parameter::constrain (const Value &value) const { @@ -170,23 +190,7 @@ Parameter::constrain (const Value &value) const if (i >= 0 && i < choices.size()) return choices[i].ident; } - int64_t selected = 0; - if (value.is_string()) { - const String text = value.as_string(); - for (size_t i = 0; i < choices.size(); i++) - if (text == choices[i].ident) - return choices[i].ident; - const String ltext = string_tolower (text); - float best = F32MAX; - for (size_t i = 0; i < choices.size(); i++) { - const size_t maxdist = std::max (choices[i].ident.size(), ltext.size()); - const float dist = damerau_levenshtein_restricted (string_tolower (choices[i].ident), ltext) / maxdist; - if (dist < best) { - best = dist; - selected = i; - } - } - } + const size_t selected = value.is_string() ? match_choice (choices, value.as_string()) : 0; return choices.size() ? choices[selected].ident : initial_; } // text @@ -356,10 +360,13 @@ Parameter::value_to_text (const Value &value) const Value Parameter::value_from_text (const String &text) const { - if (is_choice() || is_text()) + if (is_choice()) { + const ChoiceS choices = this->choices(); + return int64_t (match_choice (choices, text)); + } + if (is_text()) return constrain (text).as_string(); - else - return constrain (string_to_double (text)); + return constrain (string_to_double (text)); } // == guess_nick == diff --git a/ase/parameter.hh b/ase/parameter.hh index dfe82553..0164d8ab 100644 --- a/ase/parameter.hh +++ b/ase/parameter.hh @@ -74,6 +74,7 @@ private: StringS details_; ExtrasV extras_; Value initial_ = 0; + static size_t match_choice (const ChoiceS &choices, const String &text); }; using ParameterC = std::shared_ptr; From 26065d6431dbf2b5d670399f9034553d2f6988bb Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Sun, 29 Oct 2023 11:53:28 +0100 Subject: [PATCH 02/13] EXTERNAL: rapidjson: update Tencent/rapidjson to 2023-09-28 11:36:59 +0200 git -C external/rapidjson checkout f9d53419e912910fd8fa57d5705fa41425428c35 Among other changes, this provides kWriteNanAndInfNullFlag. Signed-off-by: Tim Janik --- external/rapidjson | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/rapidjson b/external/rapidjson index 232389d4..f9d53419 160000 --- a/external/rapidjson +++ b/external/rapidjson @@ -1 +1 @@ -Subproject commit 232389d4f1012dddec4ef84861face2d2ba85709 +Subproject commit f9d53419e912910fd8fa57d5705fa41425428c35 From e7262cb102bbfe39e8234fdb7843a2c26a0a2784 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Sun, 29 Oct 2023 13:24:06 +0100 Subject: [PATCH 03/13] JSONIPC: Makefile: fix include path to external/rapidjson/ Signed-off-by: Tim Janik --- jsonipc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonipc/Makefile b/jsonipc/Makefile index a9eaa2c2..9c1bcb78 100644 --- a/jsonipc/Makefile +++ b/jsonipc/Makefile @@ -10,7 +10,7 @@ OBJECTS = testjsonipc.o DEFS = -DSTANDALONE DEPS = jsonipc.hh Makefile -INCLUDES = -I../$(builddir)/external/ +INCLUDES = -I../external/rapidjson/include/ $(OBJECTS): %.o: %.cc $(DEPS) $(CXX) $(DEFS) $(CXXFLAGS) $(INCLUDES) -c $< From b0b7f67ff950d72421f1f4d1f695347422f44a1e Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Sun, 29 Oct 2023 13:25:49 +0100 Subject: [PATCH 04/13] =?UTF-8?q?JSONIPC:=20jsonipc.hh:=20always=20write?= =?UTF-8?q?=20null=20instead=20of=20NAN,=20=C2=B1Inf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove JsonWriteFlags from the (template) API. Signed-off-by: Tim Janik --- jsonipc/jsonipc.hh | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/jsonipc/jsonipc.hh b/jsonipc/jsonipc.hh index a70d3944..c573c0f1 100644 --- a/jsonipc/jsonipc.hh +++ b/jsonipc/jsonipc.hh @@ -28,16 +28,13 @@ namespace Jsonipc { // == Json types == using JsonValue = rapidjson::GenericValue, rapidjson::MemoryPoolAllocator >; using JsonAllocator = rapidjson::MemoryPoolAllocator; +using StringBufferWriter = rapidjson::Writer, rapidjson::UTF8<>, rapidjson::CrtAllocator, rapidjson::kWriteNanAndInfNullFlag>; static constexpr const unsigned rapidjson_parse_flags = rapidjson::kParseFullPrecisionFlag | rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag | rapidjson::kParseNanAndInfFlag | rapidjson::kParseEscapedApostropheFlag; -enum JsonWriteFlags { - STRICT = 0, - RELAXED = 1, -}; // == C++ Utilities == /// Construct a std::string with printf-like syntax, ignoring locale settings. @@ -407,19 +404,18 @@ to_json (const char (&c)[N], size_t l, JsonAllocator &allocator) } /// Simple way to generate a string from a JsonValue -template static inline std::string +static inline std::string jsonvalue_to_string (const JsonValue &value) { rapidjson::StringBuffer buffer; - constexpr unsigned FLAGS = WFLAGS & RELAXED ? rapidjson::kWriteNanAndInfFlag : 0; - rapidjson::Writer, rapidjson::UTF8<>, rapidjson::CrtAllocator, FLAGS> writer (buffer); + StringBufferWriter writer (buffer); value.Accept (writer); const std::string output { buffer.GetString(), buffer.GetSize() }; return output; } /// Generate a string from a simple JsonValue object with up to 4 members. -template static inline std::string +template static inline std::string jsonobject_to_string (const char *m1, T1 &&v1, const char *m2 = 0, T2 &&v2 = {}, const char *m3 = 0, T3 &&v3 = {}, const char *m4 = 0, T4 &&v4 = {}) { @@ -429,7 +425,7 @@ jsonobject_to_string (const char *m1, T1 &&v1, const char *m2 = 0, T2 &&v2 = {}, if (m2 && m2[0]) doc.AddMember (JsonValue (m2, a), to_json (v2, a), a); if (m3 && m3[0]) doc.AddMember (JsonValue (m3, a), to_json (v3, a), a); if (m4 && m4[0]) doc.AddMember (JsonValue (m4, a), to_json (v4, a), a); - return jsonvalue_to_string (doc); + return jsonvalue_to_string (doc); } // == CallbackInfo == @@ -1705,7 +1701,7 @@ private: d.AddMember ("id", id, a); d.AddMember ("result", result, a); // move-semantics! rapidjson::StringBuffer buffer; - rapidjson::Writer writer (buffer); + StringBufferWriter writer (buffer); d.Accept (writer); std::string output { buffer.GetString(), buffer.GetSize() }; return output; From efec4559e40425b191807de44fe564a6a9bce046 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Sun, 29 Oct 2023 13:26:21 +0100 Subject: [PATCH 05/13] ASE: jsonapi.cc: call jsonobject_to_string() directly Signed-off-by: Tim Janik --- ase/jsonapi.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ase/jsonapi.cc b/ase/jsonapi.cc index df1a4796..121b407b 100644 --- a/ase/jsonapi.cc +++ b/ase/jsonapi.cc @@ -160,7 +160,7 @@ class JsonapiConnection : public WebSocketConnection, public CustomDataContainer { JsonapiConnectionP selfp = selfw.lock(); return_unless (selfp); - const String msg = jsonobject_to_string ("method", id /*"Jsonapi/Trigger/_%%%"*/, "params", args); + const String msg = jsonobject_to_string ("method", id /*"Jsonapi/Trigger/_%%%"*/, "params", args); if (logflags & 8) selfp->log (string_format ("⬰ %s", msg)); selfp->send_text (msg); @@ -175,7 +175,7 @@ class JsonapiConnection : public WebSocketConnection, public CustomDataContainer if (selfp->is_open()) { ValueS args { id }; - const String msg = jsonobject_to_string ("method", "Jsonapi/Trigger/killed", "params", args); + const String msg = jsonobject_to_string ("method", "Jsonapi/Trigger/killed", "params", args); if (logflags & 8) selfp->log (string_format ("↚ %s", msg)); selfp->send_text (msg); From 7fd779d03fe772a0449d8364bda14e5a4e776fe0 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Sun, 29 Oct 2023 13:27:32 +0100 Subject: [PATCH 06/13] ASE: serialize.cc: call jsonvalue_to_string() directly Signed-off-by: Tim Janik --- ase/serialize.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ase/serialize.cc b/ase/serialize.cc index 340b5805..8dfb2887 100644 --- a/ase/serialize.cc +++ b/ase/serialize.cc @@ -20,7 +20,7 @@ Jsonipc::InstanceMap::Wrapper* Writ::InstanceMap::wrapper_from_json (const Jsonipc::JsonValue &value) { if (!value.IsNull()) - warning ("Ase::Writ: non persistent object cannot resolve: %s*", Jsonipc::jsonvalue_to_string (value)); + warning ("Ase::Writ: non persistent object cannot resolve: %s*", Jsonipc::jsonvalue_to_string (value)); //return nullptr; return this->Jsonipc::InstanceMap::wrapper_from_json (value); } From d011038d18b97e0279a141dde221a1964c4e271a Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Sun, 29 Oct 2023 13:27:08 +0100 Subject: [PATCH 07/13] ASE: processor.cc: remove unused choices from boolean param Signed-off-by: Tim Janik --- ase/processor.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/ase/processor.cc b/ase/processor.cc index c7c43ee1..1dedea11 100644 --- a/ase/processor.cc +++ b/ase/processor.cc @@ -285,13 +285,11 @@ AudioProcessor::add_param (Id32 id, const String &clabel, const String &nickname { assert_return (uint (id) > 0, ParamId (0)); using namespace MakeIcon; - static const ChoiceS centries { { "on"_icon, "Off" }, { "off"_icon, "On" } }; const double value = boolvalue ? 1.0 : 0.0; return add_param (id, Param { .label = clabel, .nick = nickname, .initial = value, - .extras = centries, .hints = construct_hints (hints, false, true, "toggle"), .blurb = blurb, .descr = description, From a8ee97ccbe6eb3a9fa3b0fea0f36847e785f81d4 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Sun, 29 Oct 2023 13:28:15 +0100 Subject: [PATCH 08/13] UI: b/propinput.js: give toggles preference over choices Signed-off-by: Tim Janik --- ui/b/propinput.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/b/propinput.js b/ui/b/propinput.js index 7983defe..fc6a5a2a 100644 --- a/ui/b/propinput.js +++ b/ui/b/propinput.js @@ -125,10 +125,10 @@ class BPropInput extends LitComponent { { const hints = ':' + this.prop.hints_ + ':'; let ptype = 'knob'; - if (hints.search (/:choice:/) >= 0) - ptype = 'choice'; - else if (hints.search (/:toggle:/) >= 0) + if (hints.search (/:toggle:/) >= 0) ptype = 'toggle'; + else if (hints.search (/:choice:/) >= 0) + ptype = 'choice'; if (proptype === ptype) return true; // allow html`` eval } From f69da2ffad09059119063a52f9e692187ec3b933 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Mon, 30 Oct 2023 21:09:09 +0100 Subject: [PATCH 09/13] ASE: parameter: export construct_hints(), always use "stepped" hint for bools Signed-off-by: Tim Janik --- ase/parameter.cc | 47 ++++++++++++++++++++++++++--------------------- ase/parameter.hh | 3 ++- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/ase/parameter.cc b/ase/parameter.cc index d70a454d..e017b5a0 100644 --- a/ase/parameter.cc +++ b/ase/parameter.cc @@ -31,8 +31,8 @@ Param::ExtraVals::ExtraVals (const ChoicesFunc &choicesfunc) } // == Parameter == -static String -construct_hints (const String &hints, const String &more, double pmin, double pmax) +String +Parameter::construct_hints (const String &hints, const String &more, double pmin, double pmax) { String h = hints; if (h.empty()) @@ -214,10 +214,10 @@ Parameter::constrain (const Value &value) const } static MinMaxStep -minmaxstep_from_initialval (const Param::InitialVal &iv) +minmaxstep_from_initialval (const Param::InitialVal &iv, bool *isbool) { MinMaxStep range; - std::visit ([&range] (auto &&arg) + std::visit ([&] (auto &&arg) { using T = std::decay_t; if constexpr (std::is_same_v) @@ -248,6 +248,8 @@ minmaxstep_from_initialval (const Param::InitialVal &iv) range = { 0, 0, 0 }; // strings have no numeric range else static_assert (sizeof (T) < 0, "unimplemented InitialVal type"); + if (isbool) + *isbool = std::is_same_v; }, iv); return range; } @@ -259,16 +261,18 @@ value_from_initialval (const Param::InitialVal &iv) std::visit ([&value] (auto &&arg) { using T = std::decay_t; - if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v) - value = int64_t (arg); - else if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + value = arg; + else if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) + value = int32_t (arg); + else if constexpr (std::is_same_v) + value = arg; + else if constexpr (std::is_same_v || + std::is_same_v) value = int64_t (arg); else if constexpr (std::is_same_v || std::is_same_v) @@ -306,6 +310,7 @@ Parameter::Parameter (const Param &initparam) if (!p.group.empty()) store ("group", p.group); const auto choicesp = std::get_if (&p.extras); + bool isbool = false; if (choicesfuncp) extras_ = *choicesfuncp; else if (choicesp) @@ -313,14 +318,14 @@ Parameter::Parameter (const Param &initparam) else if (fmin != fmax) extras_ = range; else - extras_ = minmaxstep_from_initialval (p.initial); + extras_ = minmaxstep_from_initialval (p.initial, &isbool); initial_ = value_from_initialval (p.initial); - if (!p.hints.empty()) { - String choice = choicesp || choicesfuncp ? "choice" : ""; - String text = choicesfuncp || initial_.is_string() ? "text" : ""; - String dynamic = choicesfuncp ? "dynamic" : ""; - store ("hints", construct_hints (p.hints, text + ":" + choice + ":" + dynamic, fmin, fmax)); - } + String hints = p.hints.empty() ? STANDARD : p.hints; + String choice = choicesp || choicesfuncp ? "choice" : ""; + String text = choicesfuncp || initial_.is_string() ? "text" : ""; + String dynamic = choicesfuncp ? "dynamic" : ""; + String stepped = isbool ? "stepped" : ""; + store ("hints", construct_hints (p.hints, text + ":" + choice + ":" + dynamic + ":" + stepped, fmin, fmax)); } String diff --git a/ase/parameter.hh b/ase/parameter.hh index 0164d8ab..d4391215 100644 --- a/ase/parameter.hh +++ b/ase/parameter.hh @@ -69,12 +69,13 @@ struct Parameter { // helpers String value_to_text (const Value &value) const; Value value_from_text (const String &text) const; + static String construct_hints (const String &hints, const String &more, double pmin = 0, double pmax = 0); + static size_t match_choice (const ChoiceS &choices, const String &text); private: using ExtrasV = std::variant; StringS details_; ExtrasV extras_; Value initial_ = 0; - static size_t match_choice (const ChoiceS &choices, const String &text); }; using ParameterC = std::shared_ptr; From ed618af2f664a286c1c7c08c1ab93a93f5c361a7 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Mon, 30 Oct 2023 21:10:25 +0100 Subject: [PATCH 10/13] ASE: processor.cc: use Parameter::construct_hints() Signed-off-by: Tim Janik --- ase/processor.cc | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/ase/processor.cc b/ase/processor.cc index 1dedea11..de5ca38c 100644 --- a/ase/processor.cc +++ b/ase/processor.cc @@ -156,23 +156,6 @@ AudioProcessor::start_group (const String &groupname) const tls_param_group = groupname; } -static CString -construct_hints (String hints, double pmin, double pmax, const String &more = "") -{ - if (hints.empty()) - hints = AudioProcessor::STANDARD; - if (hints.back() != ':') - hints = hints + ":"; - for (const auto &s : string_split (more)) - if (!s.empty() && "" == string_option_find (hints, s, "")) - hints += s + ":"; - if (hints[0] != ':') - hints = ":" + hints; - if (pmax > 0 && pmax == -pmin) - hints += "bidir:"; - return hints; -} - /// Helper for add_param() to generate the sequentially next ParamId. ParamId AudioProcessor::nextid () const @@ -227,7 +210,7 @@ AudioProcessor::add_param (Id32 id, const String &clabel, const String &nickname .initial = value, .unit = unit, .extras = MinMaxStep { pmin, pmax, 0 }, - .hints = construct_hints (hints, pmin, pmax), + .hints = Parameter::construct_hints (hints, "", pmin, pmax), .blurb = blurb, .descr = description, }, value); @@ -259,7 +242,7 @@ AudioProcessor::add_param (Id32 id, const String &clabel, const String &nickname .nick = nickname, .initial = value, .extras = centries, - .hints = construct_hints (hints, 0, pmax, "choice"), + .hints = Parameter::construct_hints (hints, "choice", 0, pmax), .blurb = blurb, .descr = description, }, value); @@ -285,15 +268,14 @@ AudioProcessor::add_param (Id32 id, const String &clabel, const String &nickname { assert_return (uint (id) > 0, ParamId (0)); using namespace MakeIcon; - const double value = boolvalue ? 1.0 : 0.0; return add_param (id, Param { .label = clabel, .nick = nickname, - .initial = value, - .hints = construct_hints (hints, false, true, "toggle"), + .initial = boolvalue, + .hints = Parameter::construct_hints (hints, "toggle", false, true), .blurb = blurb, .descr = description, - }, value); + }, boolvalue); } /// Variant of AudioProcessor::add_param() with sequential `id` generation. From e0281f6c2df6f7bad7e0e7cab56c526bf60f09f2 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Wed, 1 Nov 2023 16:27:15 +0100 Subject: [PATCH 11/13] ASE: parameter: add dconstrain() to convert to double and constrain Signed-off-by: Tim Janik --- ase/parameter.cc | 27 ++++++++++++++++++++++++--- ase/parameter.hh | 1 + 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/ase/parameter.cc b/ase/parameter.cc index e017b5a0..2d776752 100644 --- a/ase/parameter.cc +++ b/ase/parameter.cc @@ -3,6 +3,7 @@ #include "levenshtein.hh" #include "unicode.hh" #include "regex.hh" +#include "mathutils.hh" #include "internal.hh" namespace Ase { @@ -197,6 +198,24 @@ Parameter::constrain (const Value &value) const if (is_text()) return value.as_string(); // numeric + return dconstrain (value); +} + +double +Parameter::dconstrain (const Value &value) const +{ + // choices + if (is_choice()) { + const ChoiceS choices = this->choices(); + if (value.is_numeric()) { + int64_t i = value.as_int(); + if (i >= 0 && i < choices.size()) + return i; + } + const size_t selected = value.is_string() ? match_choice (choices, value.as_string()) : 0; + return choices.size() ? selected : initial_.is_numeric() ? initial_.as_double() : 0; + } + // numeric double val = value.as_double(); const auto [fmin, fmax, step] = range(); if (std::abs (fmax - fmin) < F32EPS) @@ -204,9 +223,11 @@ Parameter::constrain (const Value &value) const val = std::max (val, fmin); val = std::min (val, fmax); if (step > F32EPS && has_hint ("stepped")) { - double t = val - fmin; - t /= step; - t = std::round (t); + // round halfway cases down, so: + // 0 -> -0.5…+0.5 yields -0.5 + // 1 -> -0.5…+0.5 yields +0.5 + constexpr const auto nearintoffset = 0.5 - DOUBLE_EPSILON; // round halfway case down + const double t = std::floor ((val - fmin) / step + nearintoffset); val = fmin + t * step; val = std::min (val, fmax); } diff --git a/ase/parameter.hh b/ase/parameter.hh index d4391215..9cccbf6c 100644 --- a/ase/parameter.hh +++ b/ase/parameter.hh @@ -61,6 +61,7 @@ struct Parameter { double normalize (double val) const; double rescale (double t) const; Value constrain (const Value &value) const; + double dconstrain (const Value &value) const; void initialsync (const Value &v); /*ctor*/ Parameter () = default; /*ctor*/ Parameter (const Param&); From c8ad5610972c4b7f4a79e2360bac6a682728c849 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Wed, 1 Nov 2023 16:27:38 +0100 Subject: [PATCH 12/13] ASE: processor.cc: use parameter->dconstrain() Signed-off-by: Tim Janik --- ase/processor.cc | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/ase/processor.cc b/ase/processor.cc index de5ca38c..3efca601 100644 --- a/ase/processor.cc +++ b/ase/processor.cc @@ -322,19 +322,7 @@ AudioProcessor::set_param (Id32 paramid, const double value, bool sendnotify) ParameterC parameter = pparam->parameter; double v = value; if (parameter) - { - const auto [fmin, fmax, stepping] = parameter->range(); - v = CLAMP (v, fmin, fmax); - if (stepping > 0) - { - // round halfway cases down, so: - // 0 -> -0.5…+0.5 yields -0.5 - // 1 -> -0.5…+0.5 yields +0.5 - constexpr const auto nearintoffset = 0.5 - DOUBLE_EPSILON; // round halfway case down - v = stepping * uint64_t ((v - fmin) / stepping + nearintoffset); - v = CLAMP (fmin + v, fmin, fmax); - } - } + v = parameter->dconstrain (value); const bool need_notify = const_cast (pparam)->assign (v); if (need_notify && sendnotify && !pparam->aprop_.expired()) enotify_enqueue_mt (PARAMCHANGE); From 4df4d838ed796de840db6f2e2abe435f3d90d662 Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Wed, 1 Nov 2023 16:28:54 +0100 Subject: [PATCH 13/13] UI: b/databubble.js: fix assertion Signed-off-by: Tim Janik --- ui/b/databubble.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/b/databubble.js b/ui/b/databubble.js index 8b56640d..4fbecdc5 100644 --- a/ui/b/databubble.js +++ b/ui/b/databubble.js @@ -67,7 +67,7 @@ class DataBubbleImpl { bubble_layer() { const bubble_layer_id = '#b-shell-bubble-layer'; const el = document.body.querySelectorAll (bubble_layer_id); - console.assert (el.length, "Missing container:", bubble_layer_id); + console.assert (el.length > 0, "Missing container:", bubble_layer_id); return el[0]; } constructor() {