<!-- RUBY-BACKUP · part 33 -->
urn (window.__installed[nodeId]||[]).slice(); }catch(e){ return []; }
  };

  /* Find nodeId from a tapped object (walk up to stamped ancestor) */
  function nodeIdOf(obj){
    try{
      var o = obj;
      while(o){
        if(o.userData && o.userData.nodeId) return o.userData.nodeId;
        o = o.parent;
      }
      return null;
    }catch(e){ return null; }
  }

  /* Mini-scroll: installed codes on a block */
  var miniEl = null;
  function closeMini(){
    try{
      if(miniEl && miniEl.parentNode) miniEl.parentNode.removeChild(miniEl);
      miniEl = null;
    }catch(e){ miniEl = null; }
  }
  function openMini(nodeId, codes, sx, sy){
    closeMini();
    try{
      var el = document.createElement('div');
      var mw = 200;
      var lx = Math.max(8, Math.min(sx+10, window.innerWidth-mw-8));
      var ly = Math.max(8, Math.min(sy-40, window.innerHeight-200));
      el.style.cssText = 'position:fixed;left:'+lx+'px;top:'+ly+'px;width:'+mw+'px;'
        +'z-index:30002;background:rgba(10,8,4,0.97);border:2px solid #d8a920;border-radius:14px;'
        +'box-shadow:0 0 20px rgba(216,169,32,0.4);overflow:hidden;'
        +'max-height:0px;transition:max-height 0.22s linear;';
      var html = '<div style="padding:8px 12px 6px 12px;color:#ffd700;font-family:Georgia,serif;'
        +'font-size:12px;letter-spacing:2px;border-bottom:1px solid #8a6a1c;">'
        +'\u25C9 INSTALLED</div><div style="padding:6px;max-height:220px;overflow-y:auto;-webkit-overflow-scrolling:touch;">';
      for(var i=0;i<codes.length;i++){
        html += '<div class="cps-minirow" data-code="'+codes[i].replace(/"/g,'&quot;')+'" '
          +'style="padding:10px;margin:2px 0;border-radius:8px;cursor:pointer;color:#ffd700;'
          +'font-family:Georgia,serif;font-size:14px;-webkit-tap-highlight-color:transparent;">'
          +'\u25B8 '+codes[i]+'</div>';
      }
      html += '<div class="cps-minirow" data-code="__uninstall" '
        +'style="padding:10px;margin:6px 0 2px 0;border-radius:8px;cursor:pointer;color:#ff8888;'
        +'font-family:Georgia,serif;font-size:13px;font-style:italic;border-top:1px solid rgba(138,106,28,0.4);">'
        +'remove all</div>';
      html += '</div>';
      el.innerHTML = html;
      document.body.appendChild(el);
      miniEl = el;
      var rows = el.querySelectorAll('.cps-minirow');
      for(var k=0;k<rows.length;k++){
        (function(row){
          var fired = false;
          function go(e){
            if(fired) return; fired = true;
            try{ if(e){ e.preventDefault(); e.stopPropagation(); } }catch(x){}
            var c = row.getAttribute('data-code');
            closeMini();
            if(c === '__uninstall'){
              window.uninstallCodesOn(nodeId);
              try{ if(typeof codeStatus==='function') codeStatus('codes removed from block'); }catch(e){}
              return;
            }
            setTimeout(function(){
              try{
                if(typeof window.runLine==='function') window.runLine(c);
                else if(typeof Mainframe!=='undefined' && Mainframe.run) Mainframe.run(c);
              }catch(e){}
            }, 60);
          }
          row.addEventListener('touchstart', go, {passive:false});
          row.addEventListener('mousedown', go);
        })(rows[k]);
      }
      void el.offsetHeight;
      el.style.maxHeight = Math.min(280, 60+codes.length*44)+'px';
      // Auto-close after 8s
      setTimeout(closeMini, 8000);
    }catch(e){ closeMini(); }
  }

  /* Arm install: called from the scroll when INSTALL mode is on */
  window.armInstall = function(code, why){
    window.__installArmed = {code:code, why:why};
    window.__uninstallArmed = false;
    closeScroll();
    try{ if(typeof codeStatus==='function') codeStatus('tap a block \u2014 install "'+code+'"', true); }catch(e){}
  };
  window.armUninstall = function(){
    window.__uninstallArmed = true;
    window.__installArmed = null;
    closeScroll();
    try{ if(typeof codeStatus==='function') codeStatus('tap a block \u2014 remove its codes', true); }catch(e){}
  };
  window.cancelArm = function(){
    window.__installArmed = null;
    window.__uninstallArmed = false;
    try{ if(typeof codeStatus==='function') codeStatus(''); }catch(e){}
  };

  /* Install variant of the arg prompt: builds the full code then arms install */
  function promptArgInstall(sec, cd, rowEl){
    try{
      var old = document.getElementById('cps-argprompt');
      if(old && old.parentNode) old.parentNode.removeChild(old);
      var pr = document.createElement('div');
      pr.id = 'cps-argprompt';
      pr.style.cssText = 'padding:8px 10px;background:rgba(216,169,32,0.08);border-radius:10px;margin:4px 0;';
      pr.innerHTML =
        '<div style="color:#ffd700;font-size:13px;margin-bottom:6px;font-style:italic;">install '+cd.code+' — type '+(cd.arg||'value')+':</div>'
        +'<div style="display:flex;gap:8px;">'
        +'<input id="cps-argin" type="text" p