     (html/div {:style {:height "120px"}}
       (html/input {:ref (util/comp-fn this ::editor-ref attach-codemirror)}))))
 
-(defsc RuntimePage
+(defsc RuntimeTapPage
   [this {::keys [rid] :as props}]
   {:ident
    (fn []

  {:class Page
   :factory ui-page})

(routing/register ::ui-model/root-router ::rid
  {:class RuntimePage
   :factory ui-runtime-page})

(defn route [tokens]
  (if-not @tool-ref

           (refresh-ui [[::oid oid]])
           )}))))

(defn get-state-ref []
  (::fa/state-atom env/app))

(defn ensure-runtime-init [rid pending-tx callback]
  (let [state-ref (get-state-ref)]
    (if (get-in @state-ref [::rid rid ::supported-ops])
      (callback)

      (do (swap! state-ref update-in [::rid rid] merge {::rid rid})
          (fc/transact! env/app pending-tx)
          (call! {:op :request-supported-ops :rid rid}
            {:runtime-not-found
             (fn [msg]
               ;; FIXME: redirect somewhere neutral
               (js/console.warn "runtime-not-found" rid))

             :supported-ops
             (fn [{:keys [ops] :as msg}]
               (swap! state-ref assoc-in [::rid rid ::supported-ops] ops)
               (refresh-ui [[::rid rid]])
               (callback))})))))

(defmutation select-runtime-page [{:keys [rid] :as params}]
  (action [{:keys [state] :as env}]
    (swap! state update-in [::runtime-page rid] merge {::rid rid ::runtime [::rid rid]})))

(defmutation select-runtime-tap-page [{:keys [rid] :as params}]
  (action [{:keys [state] :as env}]
    (swap! state update-in [::tap-page rid] merge {::rid rid ::runtime [::rid rid]})

    ;; FIXME: allow having multiple active?
    (let [previous-rid (::active-rid @state)]

      (swap! state
        (fn [state]
          (-> state
              (assoc ::active-rid rid)
              (update-in [::tap-page rid] dissoc ::object)
              (update-in [::tap-page rid] assoc ::nav-stack []))))

      (when (contains? (get-in @state [::rid rid ::supported-ops]) :request-tap-history)
        (when (not= rid previous-rid)
          (when previous-rid ;; may be nil
            (cast! {:op :tap-unsubscribe :rid previous-rid}))
          (cast! {:op :tap-subscribe :rid rid}))

        (call! {:op :request-tap-history :rid rid :num 100}
          {:tap-history
           (fn [{:keys [rid oids] :as msg}]
             (let [data @state]
               (doseq [oid oids
                       :when (not (get-in data [::oid oid :summary]))]
                 (add-new-tap-obj state rid oid)))

             (swap! state assoc-in [::tap-page rid ::objects] (as-idents ::oid oids))
             (refresh-ui [[::tap-page rid]])
             )}))
      )))

(defmutation unselect-object [{:keys [oid] :as params}]
  (action [{:keys [state] :as env}]
