Nnnnn export bindable object with different (#12779) a680ab80e5
* chore(runtime): remove unused list methods
* fix(editor): export data bind if primary is not found

Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head
index b7d8638..9769a18 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-76eeeed5d987e8129ae4393e57b1db097607bbc9
+a680ab80e5dce5aaf304a0e9a1c5ef6319762335
diff --git a/include/rive/artboard_component_list.hpp b/include/rive/artboard_component_list.hpp
index bc90480..e676e4e 100644
--- a/include/rive/artboard_component_list.hpp
+++ b/include/rive/artboard_component_list.hpp
@@ -114,8 +114,6 @@
         invalidateOrderedListIndicesCache();
     }
     void shouldResetInstances(bool value) { m_shouldResetInstances = value; }
-    bool useStatefulInstances() const { return m_useStatefulInstances; }
-    void useStatefulInstances(bool value) { m_useStatefulInstances = value; }
     void setVirtualizablePosition(int index, Vec2D position) override;
     void createArtboardAt(int index, bool forceLayoutSync = true);
     void addArtboardAt(std::unique_ptr<ArtboardInstance> artboard,
@@ -198,21 +196,10 @@
         m_artboardOverridesMap;
     std::unordered_map<int, int> m_artboardMapRules;
 
-    // Data binds that bridge properties between a stateful component's
-    // cloned ViewModelInstance and the original (user-provided) one.
-    // Keyed by list item so they can be cleaned up when the item is removed.
-    std::unordered_map<rcp<ViewModelInstanceListItem>,
-                       std::vector<std::unique_ptr<DataBind>>>
-        m_bridgeDataBinds;
-    void createBridgeBinds(rcp<ViewModelInstanceListItem> listItem,
-                           ViewModelInstance* original,
-                           ViewModelInstance* clone);
-    void removeBridgeBinds(const rcp<ViewModelInstanceListItem>& listItem);
     void attachArtboardOverride(ArtboardInstance*,
                                 rcp<ViewModelInstanceListItem>);
     void clearArtboardOverride(ArtboardInstance*);
     bool m_shouldResetInstances = false;
-    bool m_useStatefulInstances = false;
     bool listsAreEqual(std::vector<rcp<ViewModelInstanceListItem>>* list,
                        std::vector<rcp<ViewModelInstanceListItem>>* compared);
 
diff --git a/src/artboard_component_list.cpp b/src/artboard_component_list.cpp
index 4a7bc0e..f80b773 100644
--- a/src/artboard_component_list.cpp
+++ b/src/artboard_component_list.cpp
@@ -121,22 +121,6 @@
         }
     }
 
-    // Clean up bridge binds FIRST since they reference VM instances
-    // that may be owned by the artboard instances below.
-    auto* parentAb = artboard();
-    for (auto& [item, binds] : m_bridgeDataBinds)
-    {
-        for (auto& bind : binds)
-        {
-            bind->unbind();
-            if (parentAb != nullptr)
-            {
-                parentAb->removeDataBind(bind.get());
-            }
-        }
-    }
-    m_bridgeDataBinds.clear();
-
     // Destroy state machines BEFORE artboards.
     // StateMachineInstance owns FocusListenerGroup objects that hold raw
     // pointers to FocusData (owned by artboards). Destroying artboards first
@@ -1263,56 +1247,8 @@
     {
         auto mainArtboard = this->artboard();
         auto dataContext = mainArtboard->dataContext();
-        rcp<ViewModelInstance> viewModelInstance = nullptr;
-
-        if (m_file != nullptr)
-        {
-            auto source = artboardInstance->artboardSource();
-            if (m_useStatefulInstances && source != nullptr)
-            {
-                auto listItemInstance = listItem->viewModelInstance();
-                if (listItemInstance != nullptr)
-                {
-                    auto copy = rcp<ViewModelInstance>(
-                        listItemInstance->clone()->as<ViewModelInstance>());
-                    m_file->completeViewModelProperties(copy.get());
-#ifdef WITH_RIVE_TOOLS
-                    if (copy)
-                    {
-                        m_file->registerViewModelInstance(copy.get(), copy);
-                    }
-#endif
-                    viewModelInstance = copy;
-
-                    // Create bridge data binds between the original and
-                    // cloned VM instances for input/output properties.
-                    createBridgeBinds(listItem,
-                                      listItemInstance.get(),
-                                      copy.get());
-                }
-                else
-                {
-                    auto viewModel = m_file->viewModel(source->viewModelId());
-                    if (viewModel != nullptr)
-                    {
-                        viewModelInstance =
-                            m_file->createDefaultViewModelInstance(viewModel);
-                    }
-                    // Store the default instance on the list item so we
-                    // don't recreate one every time.
-                    if (viewModelInstance != nullptr)
-                    {
-                        listItem->viewModelInstance(viewModelInstance);
-                    }
-                }
-            }
-        }
-
-        // Fall back to the list item's VM instance if not stateful.
-        if (viewModelInstance == nullptr)
-        {
-            viewModelInstance = listItem->viewModelInstance();
-        }
+        rcp<ViewModelInstance> viewModelInstance =
+            listItem->viewModelInstance();
 
         // TODO: @hernan added this to make sure data binds are procesed in the
         // current frame instead of waiting for the next run. But might not be
@@ -1357,8 +1293,6 @@
         }
         clearArtboardOverride(itr->second.get());
     }
-    // Remove bridge data binds before destroying the artboard.
-    removeBridgeBinds(item);
     // Destroy state machines BEFORE artboards to ensure FocusListenerGroup
     // can unregister from FocusData before the artboard (and its FocusData)
     // is destroyed. Otherwise we get use-after-free in ~FocusListenerGroup.
@@ -1366,136 +1300,6 @@
     m_artboardInstancesMap.erase(item);
 }
 
-/// Returns the propertyValuePropertyKey for a ViewModelInstanceValue based
-/// on its core type, or Core::invalidPropertyKey if unsupported.
-static uint16_t propertyValueKeyForType(uint16_t coreType)
-{
-    switch (coreType)
-    {
-        case ViewModelInstanceNumberBase::typeKey:
-            return ViewModelInstanceNumberBase::propertyValuePropertyKey;
-        case ViewModelInstanceStringBase::typeKey:
-            return ViewModelInstanceStringBase::propertyValuePropertyKey;
-        case ViewModelInstanceColorBase::typeKey:
-            return ViewModelInstanceColorBase::propertyValuePropertyKey;
-        case ViewModelInstanceBooleanBase::typeKey:
-            return ViewModelInstanceBooleanBase::propertyValuePropertyKey;
-        case ViewModelInstanceEnumBase::typeKey:
-            return ViewModelInstanceEnumBase::propertyValuePropertyKey;
-        case ViewModelInstanceTriggerBase::typeKey:
-            return ViewModelInstanceTriggerBase::propertyValuePropertyKey;
-        case ViewModelInstanceViewModelBase::typeKey:
-            return ViewModelInstanceViewModelBase::propertyValuePropertyKey;
-        default:
-            return Core::invalidPropertyKey;
-    }
-}
-
-void ArtboardComponentList::createBridgeBinds(
-    rcp<ViewModelInstanceListItem> listItem,
-    ViewModelInstance* original,
-    ViewModelInstance* clone)
-{
-    if (original == nullptr || clone == nullptr)
-    {
-        return;
-    }
-    auto* vm = clone->viewModel();
-    if (vm == nullptr)
-    {
-        return;
-    }
-    auto* parentArtboard = artboard();
-    if (parentArtboard == nullptr)
-    {
-        return;
-    }
-
-    auto& binds = m_bridgeDataBinds[listItem];
-
-    for (auto& cloneValueRcp : clone->propertyValues())
-    {
-        auto* cloneValue = cloneValueRcp.get();
-        auto* prop = cloneValue->viewModelProperty();
-        if (prop == nullptr || (!prop->isInput() && !prop->isOutput()))
-        {
-            continue;
-        }
-
-        // Find the matching property on the original by ViewModelProperty
-        // pointer (both instances share the same ViewModel definition).
-        ViewModelInstanceValue* originalValue = nullptr;
-        for (auto& origRcp : original->propertyValues())
-        {
-            if (origRcp->viewModelProperty() == prop)
-            {
-                originalValue = origRcp.get();
-                break;
-            }
-        }
-        if (originalValue == nullptr)
-        {
-            continue;
-        }
-
-        auto propKey = propertyValueKeyForType(cloneValue->coreType());
-        if (propKey == Core::invalidPropertyKey)
-        {
-            continue;
-        }
-
-        if (prop->isInput())
-        {
-            // Input: original → clone (source to target)
-            auto bind = std::make_unique<DataBind>();
-            bind->source(ref_rcp(originalValue));
-            bind->target(cloneValue);
-            bind->propertyKey(propKey);
-            bind->flags(static_cast<uint32_t>(DataBindFlags::ToTarget));
-            bind->bind();
-            parentArtboard->addDataBind(bind.get());
-            binds.push_back(std::move(bind));
-        }
-
-        if (prop->isOutput())
-        {
-            // Output: clone → original. Uses ToSource direction so the
-            // bind is in the persisting list and continuously syncs
-            // changes made by the component's state machine back to
-            // the user-provided VM instance.
-            // ToSource semantics: reads from target, writes to source.
-            auto bind = std::make_unique<DataBind>();
-            bind->source(ref_rcp(originalValue));
-            bind->target(cloneValue);
-            bind->propertyKey(propKey);
-            bind->flags(static_cast<uint32_t>(DataBindFlags::ToSource));
-            bind->bind();
-            parentArtboard->addDataBind(bind.get());
-            binds.push_back(std::move(bind));
-        }
-    }
-}
-
-void ArtboardComponentList::removeBridgeBinds(
-    const rcp<ViewModelInstanceListItem>& listItem)
-{
-    auto itr = m_bridgeDataBinds.find(listItem);
-    if (itr == m_bridgeDataBinds.end())
-    {
-        return;
-    }
-    auto* parentArtboard = artboard();
-    for (auto& bind : itr->second)
-    {
-        bind->unbind();
-        if (parentArtboard != nullptr)
-        {
-            parentArtboard->removeDataBind(bind.get());
-        }
-    }
-    m_bridgeDataBinds.erase(itr);
-}
-
 void ArtboardComponentList::createArtboardRecorders(const Artboard* artboard)
 {
     if (artboard == nullptr)