       (when (contains? supported-ops :eval-clj)
         (ui-runtime-eval this props)))))
 
+(def ui-runtime-tap-page (fc/factory RuntimeTapPage {}))
+
+(defsc RuntimePage
+  [this {::keys [rid] :as props}]
+  {:ident
+   (fn []
+     [::runtime-page rid])
+
+   :query
+   (fn []
+     [::rid
+      {::runtime [::runtime-info
+                  ::supported-ops]}])}
+
+  (let [{::keys [runtime]} props
+        {::keys [runtime-info supported-ops]} runtime]
+    (html/fragment
+      (html/div
+        (html/h1 "Runtime Overview"))
+
+      (html/div (pr-str runtime-info))
+      (html/div (pr-str supported-ops))
+      (html/div {:className "flex-1"}
+        ))))
+
 (def ui-runtime-page (fc/factory RuntimePage {}))
 
 (defsc Page [this {::keys [runtimes runtime] :as props}]

           {:router ::ui-model/root-router
            :ident [::ui-model/page-inspect 1]})])

      (let [[rid & more] tokens
            rid (js/parseInt rid)]
        (fc/transact! env/app
          [(select-runtime {:rid rid})
           (routing/set-route {:router ::ui-model/root-router
                               :ident [::rid (js/parseInt rid)]})])))))

           (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}]
