Loading editor/src/webedit_api.cpp +68 −10 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ #include <unordered_map> #include <regex> #include <cctype> #include <mutex> #include <htmlpp/html.h> #include <httppp/exception.h> Loading Loading @@ -135,8 +136,11 @@ bool queryNodeWidgetDescriptor(const std::string &scriptPath, NodeWidgetDescript return ok; } std::vector<NodeWidgetDescriptor> nodeWidgetCatalog() { std::vector<NodeWidgetDescriptor> catalog; const std::vector<NodeWidgetDescriptor> &nodeWidgetCatalog() { static std::vector<NodeWidgetDescriptor> catalog; static std::once_flag initialized; std::call_once(initialized, []() { std::vector<std::string> widgetDirs; const std::string dirsEnv = getEnvOrDefault("BLOGI_WEBEDIT_NODE_WIDGET_DIRS", ""); Loading Loading @@ -183,6 +187,16 @@ std::vector<NodeWidgetDescriptor> nodeWidgetCatalog() { desc.version = "1.0"; desc.canHaveChildren = false; // Prefer metadata from the widget itself (incl. hardcoded UUID/type_id). NodeWidgetDescriptor meta = desc; if (queryNodeWidgetDescriptor(desc.scriptPath, meta)) { if (!meta.name.empty()) desc.name = meta.name; if (!meta.typeId.empty()) desc.typeId = meta.typeId; if (!meta.author.empty()) desc.author = meta.author; if (!meta.version.empty()) desc.version = meta.version; desc.canHaveChildren = meta.canHaveChildren; } if (desc.name.empty()) continue; Loading @@ -205,6 +219,8 @@ std::vector<NodeWidgetDescriptor> nodeWidgetCatalog() { return a.name < b.name; }); }); return catalog; } Loading @@ -215,7 +231,7 @@ bool findNodeWidgetByName(const std::string &name, NodeWidgetDescriptor &out) { return value; }; const auto catalog = nodeWidgetCatalog(); const auto &catalog = nodeWidgetCatalog(); // Fast path: exact match. auto it = std::find_if(catalog.begin(), catalog.end(), [&](const NodeWidgetDescriptor &d) { Loading @@ -239,6 +255,31 @@ bool findNodeWidgetByName(const std::string &name, NodeWidgetDescriptor &out) { return true; } bool findNodeWidgetByTypeId(const std::string &typeId, NodeWidgetDescriptor &out) { if (typeId.empty()) { return false; } auto lower = [](std::string value) { std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); }); return value; }; const std::string wanted = lower(typeId); const auto &catalog = nodeWidgetCatalog(); auto it = std::find_if(catalog.begin(), catalog.end(), [&](const NodeWidgetDescriptor &d) { return lower(d.typeId) == wanted; }); if (it == catalog.end()) { return false; } out = *it; return true; } bool isNodeWidgetPluginName(const std::string &name) { return name == "NodeWidget" || name == "Nodewidget"; } Loading Loading @@ -376,7 +417,10 @@ void webedit::Api::loadDocumentTreeFromXml(DocumentState &doc, const std::string continue; const char *typeName = child->Name(); auto *el = createPluginByName(typeName, doc); const char *typeIdAttr = child->Attribute("type_id"); auto *el = createPluginByName(typeName, doc, typeIdAttr ? std::string(typeIdAttr) : std::string()); if (!el) continue; Loading Loading @@ -665,7 +709,7 @@ void webedit::Api::handleGetPlugins(libhttppp::HttpRequest &curreq) { json_object *arr = json_object_new_array(); try { const auto catalog = nodeWidgetCatalog(); const auto &catalog = nodeWidgetCatalog(); for (const auto &desc : catalog) { json_object *obj = json_object_new_object(); json_object_object_add(obj, "name", json_object_new_string(desc.name.c_str())); Loading Loading @@ -1321,7 +1365,10 @@ void webedit::Api::handleImportXml(libhttppp::HttpRequest &curreq, blogi::webedit::EditPlugin *lastTop = nullptr; for (auto *child = rootEl->FirstChildElement(); child; child = child->NextSiblingElement()) { const char *typeName = child->Name(); auto *el = createPluginByName(typeName, doc); const char *typeIdAttr = child->Attribute("type_id"); auto *el = createPluginByName(typeName, doc, typeIdAttr ? std::string(typeIdAttr) : std::string()); if (!el) continue; el->fromXmlObject(child); Loading Loading @@ -1440,7 +1487,10 @@ void webedit::Api::handlePasteElement(libhttppp::HttpRequest &curreq, child = child->NextSiblingElement()) { if (strcmp(child->Name(), "property") == 0) continue; const char *typeName = child->Name(); auto *el = createPluginByName(typeName, doc); const char *typeIdAttr = child->Attribute("type_id"); auto *el = createPluginByName(typeName, doc, typeIdAttr ? std::string(typeIdAttr) : std::string()); if (!el) continue; el->fromXmlObject(child); Loading Loading @@ -3185,10 +3235,15 @@ webedit::Api::NodeContext webedit::Api::findNodeContext( } blogi::webedit::EditPlugin *webedit::Api::createPluginByName( const std::string &name, DocumentState &doc) { const std::string &name, DocumentState &doc, const std::string &typeIdHint) { NodeWidgetDescriptor desc; if (!findNodeWidgetByName(name, desc)) // Preferred mapping for persisted templates: unique hardcoded type_id. if (!typeIdHint.empty() && findNodeWidgetByTypeId(typeIdHint, desc)) { // resolved by UUID } else if (!findNodeWidgetByName(name, desc)) { return nullptr; } auto *newEl = new EmbeddedNodeWidget(desc); if (!newEl) Loading @@ -3211,7 +3266,10 @@ void webedit::Api::loadChildrenFromXml(tinyxml2::XMLElement *parentXml, continue; const char *typeName = childXml->Name(); auto *childEl = createPluginByName(typeName, doc); const char *typeIdAttr = childXml->Attribute("type_id"); auto *childEl = createPluginByName(typeName, doc, typeIdAttr ? std::string(typeIdAttr) : std::string()); if (!childEl) continue; childEl->fromXmlObject(childXml); Loading editor/src/webedit_api.h +2 −1 Original line number Diff line number Diff line Loading @@ -169,7 +169,8 @@ namespace webedit { NodeContext findNodeContext(blogi::webedit::EditPlugin *root, const std::string &uuid); blogi::webedit::EditPlugin *createPluginByName(const std::string &name, DocumentState &doc); DocumentState &doc, const std::string &typeIdHint = ""); void loadChildrenFromXml(tinyxml2::XMLElement *parentXml, blogi::webedit::EditPlugin *parentEl, Loading Loading
editor/src/webedit_api.cpp +68 −10 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ #include <unordered_map> #include <regex> #include <cctype> #include <mutex> #include <htmlpp/html.h> #include <httppp/exception.h> Loading Loading @@ -135,8 +136,11 @@ bool queryNodeWidgetDescriptor(const std::string &scriptPath, NodeWidgetDescript return ok; } std::vector<NodeWidgetDescriptor> nodeWidgetCatalog() { std::vector<NodeWidgetDescriptor> catalog; const std::vector<NodeWidgetDescriptor> &nodeWidgetCatalog() { static std::vector<NodeWidgetDescriptor> catalog; static std::once_flag initialized; std::call_once(initialized, []() { std::vector<std::string> widgetDirs; const std::string dirsEnv = getEnvOrDefault("BLOGI_WEBEDIT_NODE_WIDGET_DIRS", ""); Loading Loading @@ -183,6 +187,16 @@ std::vector<NodeWidgetDescriptor> nodeWidgetCatalog() { desc.version = "1.0"; desc.canHaveChildren = false; // Prefer metadata from the widget itself (incl. hardcoded UUID/type_id). NodeWidgetDescriptor meta = desc; if (queryNodeWidgetDescriptor(desc.scriptPath, meta)) { if (!meta.name.empty()) desc.name = meta.name; if (!meta.typeId.empty()) desc.typeId = meta.typeId; if (!meta.author.empty()) desc.author = meta.author; if (!meta.version.empty()) desc.version = meta.version; desc.canHaveChildren = meta.canHaveChildren; } if (desc.name.empty()) continue; Loading @@ -205,6 +219,8 @@ std::vector<NodeWidgetDescriptor> nodeWidgetCatalog() { return a.name < b.name; }); }); return catalog; } Loading @@ -215,7 +231,7 @@ bool findNodeWidgetByName(const std::string &name, NodeWidgetDescriptor &out) { return value; }; const auto catalog = nodeWidgetCatalog(); const auto &catalog = nodeWidgetCatalog(); // Fast path: exact match. auto it = std::find_if(catalog.begin(), catalog.end(), [&](const NodeWidgetDescriptor &d) { Loading @@ -239,6 +255,31 @@ bool findNodeWidgetByName(const std::string &name, NodeWidgetDescriptor &out) { return true; } bool findNodeWidgetByTypeId(const std::string &typeId, NodeWidgetDescriptor &out) { if (typeId.empty()) { return false; } auto lower = [](std::string value) { std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); }); return value; }; const std::string wanted = lower(typeId); const auto &catalog = nodeWidgetCatalog(); auto it = std::find_if(catalog.begin(), catalog.end(), [&](const NodeWidgetDescriptor &d) { return lower(d.typeId) == wanted; }); if (it == catalog.end()) { return false; } out = *it; return true; } bool isNodeWidgetPluginName(const std::string &name) { return name == "NodeWidget" || name == "Nodewidget"; } Loading Loading @@ -376,7 +417,10 @@ void webedit::Api::loadDocumentTreeFromXml(DocumentState &doc, const std::string continue; const char *typeName = child->Name(); auto *el = createPluginByName(typeName, doc); const char *typeIdAttr = child->Attribute("type_id"); auto *el = createPluginByName(typeName, doc, typeIdAttr ? std::string(typeIdAttr) : std::string()); if (!el) continue; Loading Loading @@ -665,7 +709,7 @@ void webedit::Api::handleGetPlugins(libhttppp::HttpRequest &curreq) { json_object *arr = json_object_new_array(); try { const auto catalog = nodeWidgetCatalog(); const auto &catalog = nodeWidgetCatalog(); for (const auto &desc : catalog) { json_object *obj = json_object_new_object(); json_object_object_add(obj, "name", json_object_new_string(desc.name.c_str())); Loading Loading @@ -1321,7 +1365,10 @@ void webedit::Api::handleImportXml(libhttppp::HttpRequest &curreq, blogi::webedit::EditPlugin *lastTop = nullptr; for (auto *child = rootEl->FirstChildElement(); child; child = child->NextSiblingElement()) { const char *typeName = child->Name(); auto *el = createPluginByName(typeName, doc); const char *typeIdAttr = child->Attribute("type_id"); auto *el = createPluginByName(typeName, doc, typeIdAttr ? std::string(typeIdAttr) : std::string()); if (!el) continue; el->fromXmlObject(child); Loading Loading @@ -1440,7 +1487,10 @@ void webedit::Api::handlePasteElement(libhttppp::HttpRequest &curreq, child = child->NextSiblingElement()) { if (strcmp(child->Name(), "property") == 0) continue; const char *typeName = child->Name(); auto *el = createPluginByName(typeName, doc); const char *typeIdAttr = child->Attribute("type_id"); auto *el = createPluginByName(typeName, doc, typeIdAttr ? std::string(typeIdAttr) : std::string()); if (!el) continue; el->fromXmlObject(child); Loading Loading @@ -3185,10 +3235,15 @@ webedit::Api::NodeContext webedit::Api::findNodeContext( } blogi::webedit::EditPlugin *webedit::Api::createPluginByName( const std::string &name, DocumentState &doc) { const std::string &name, DocumentState &doc, const std::string &typeIdHint) { NodeWidgetDescriptor desc; if (!findNodeWidgetByName(name, desc)) // Preferred mapping for persisted templates: unique hardcoded type_id. if (!typeIdHint.empty() && findNodeWidgetByTypeId(typeIdHint, desc)) { // resolved by UUID } else if (!findNodeWidgetByName(name, desc)) { return nullptr; } auto *newEl = new EmbeddedNodeWidget(desc); if (!newEl) Loading @@ -3211,7 +3266,10 @@ void webedit::Api::loadChildrenFromXml(tinyxml2::XMLElement *parentXml, continue; const char *typeName = childXml->Name(); auto *childEl = createPluginByName(typeName, doc); const char *typeIdAttr = childXml->Attribute("type_id"); auto *childEl = createPluginByName(typeName, doc, typeIdAttr ? std::string(typeIdAttr) : std::string()); if (!childEl) continue; childEl->fromXmlObject(childXml); Loading
editor/src/webedit_api.h +2 −1 Original line number Diff line number Diff line Loading @@ -169,7 +169,8 @@ namespace webedit { NodeContext findNodeContext(blogi::webedit::EditPlugin *root, const std::string &uuid); blogi::webedit::EditPlugin *createPluginByName(const std::string &name, DocumentState &doc); DocumentState &doc, const std::string &typeIdHint = ""); void loadChildrenFromXml(tinyxml2::XMLElement *parentXml, blogi::webedit::EditPlugin *parentEl, Loading