mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 19:28:12 +00:00
🐛 Fix MCP active tab switching (#8856)
This commit is contained in:
parent
67734c5835
commit
e99b6ec213
@ -57,5 +57,6 @@
|
||||
[type data]
|
||||
(ptk/reify ::event
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(emit! type data))))
|
||||
(effect [_ state _]
|
||||
(let [session-id (get state :session-id)]
|
||||
(emit! session-id type data)))))
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
[app.common.types.variant :as ctv]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.broadcast :as mbc]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.comments :as dcmt]
|
||||
[app.main.data.common :as dcm]
|
||||
@ -213,9 +212,11 @@
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dp/check-open-plugin)
|
||||
(fdf/fix-deleted-fonts-for-local-library file-id)
|
||||
(mcp/init-mcp-connection)))))
|
||||
(rx/merge
|
||||
(rx/of (dp/check-open-plugin)
|
||||
(fdf/fix-deleted-fonts-for-local-library file-id))
|
||||
(when (contains? cf/flags :mcp)
|
||||
(rx/of (mcp/init)))))))
|
||||
|
||||
(defn- bundle-fetched
|
||||
[{:keys [file file-id thumbnails] :as bundle}]
|
||||
@ -319,180 +320,169 @@
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
|
||||
(->> (rx/merge
|
||||
(rx/concat
|
||||
;; Fetch all essential data that should be loaded before the file
|
||||
(rx/merge
|
||||
(if ^boolean render-wasm?
|
||||
(->> (rx/from @wasm/module)
|
||||
(rx/filter true?)
|
||||
(rx/tap (fn [_]
|
||||
(let [event (ug/event "penpot:wasm:loaded")]
|
||||
(ug/dispatch! event))))
|
||||
(rx/ignore))
|
||||
(rx/empty))
|
||||
(rx/concat
|
||||
(->> (rx/merge
|
||||
(rx/concat
|
||||
;; Fetch all essential data that should be loaded before the file
|
||||
(rx/merge
|
||||
(if ^boolean render-wasm?
|
||||
(->> (rx/from @wasm/module)
|
||||
(rx/filter true?)
|
||||
(rx/tap (fn [_]
|
||||
(let [event (ug/event "penpot:wasm:loaded")]
|
||||
(ug/dispatch! event))))
|
||||
(rx/ignore))
|
||||
(rx/empty))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::df/fonts-loaded))
|
||||
(rx/take 1)
|
||||
(rx/ignore))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::df/fonts-loaded))
|
||||
(rx/take 1)
|
||||
(rx/ignore))
|
||||
|
||||
(rx/of (ntf/hide)
|
||||
(dcmt/retrieve-comment-threads file-id)
|
||||
(dcmt/fetch-profiles)
|
||||
(df/fetch-fonts team-id))
|
||||
(rx/of (ntf/hide)
|
||||
(dcmt/retrieve-comment-threads file-id)
|
||||
(dcmt/fetch-profiles)
|
||||
(df/fetch-fonts team-id))
|
||||
|
||||
(when (contains? cf/flags :mcp)
|
||||
(rx/of (du/fetch-access-tokens))))
|
||||
(when (contains? cf/flags :mcp)
|
||||
(rx/of (du/fetch-access-tokens))))
|
||||
|
||||
;; Once the essential data is fetched, lets proceed to
|
||||
;; fetch teh file bunldle
|
||||
(rx/of (fetch-bundle file-id features)))
|
||||
;; Once the essential data is fetched, lets proceed to
|
||||
;; fetch teh file bunldle
|
||||
(rx/of (fetch-bundle file-id features)))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::bundle-fetched))
|
||||
(rx/take 1)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [file]}]
|
||||
(log/debug :hint "bundle fetched"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::bundle-fetched))
|
||||
(rx/take 1)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [file]}]
|
||||
(log/debug :hint "bundle fetched"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
|
||||
(rx/of (dpj/initialize-project (:project-id file))
|
||||
(dwn/initialize team-id file-id)
|
||||
(dwsl/initialize-shape-layout)
|
||||
(fetch-libraries file-id features)
|
||||
(-> (workspace-initialized file-id)
|
||||
(with-meta {:team-id team-id
|
||||
:file-id file-id}))))))
|
||||
(rx/of (dpj/initialize-project (:project-id file))
|
||||
(dwn/initialize team-id file-id)
|
||||
(dwsl/initialize-shape-layout)
|
||||
(fetch-libraries file-id features)
|
||||
(-> (workspace-initialized file-id)
|
||||
(with-meta {:team-id team-id
|
||||
:file-id file-id}))))))
|
||||
|
||||
;; Install dev perf observers once the workspace is ready
|
||||
(when (contains? cf/flags :perf-logs)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/take 1)
|
||||
(rx/tap (fn [_] (perf/setup)))))
|
||||
;; Install dev perf observers once the workspace is ready
|
||||
(when (contains? cf/flags :perf-logs)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/take 1)
|
||||
(rx/tap (fn [_] (perf/setup)))))
|
||||
|
||||
(when (contains? cf/flags :mcp)
|
||||
(->> mbc/stream
|
||||
(rx/filter (mbc/type? :mcp-enabled-change-connection))
|
||||
(rx/map deref)
|
||||
(rx/mapcat (fn [value]
|
||||
(rx/of (mcp/update-mcp-connection value)
|
||||
(mcp/user-disconnect-mcp))))))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dps/persistence-notification))
|
||||
(rx/take 1)
|
||||
(rx/map dwc/set-workspace-visited))
|
||||
|
||||
(when (contains? cf/flags :mcp)
|
||||
(->> mbc/stream
|
||||
(rx/filter (mbc/type? :mcp-enabled-change-status))
|
||||
(rx/map deref)
|
||||
(rx/map mcp/update-mcp-status)))
|
||||
(when-let [component-id (some-> rparams :component-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwl/go-to-local-component :id component-id :update-layout? (:update-layout rparams)))))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dps/persistence-notification))
|
||||
(rx/take 1)
|
||||
(rx/map dwc/set-workspace-visited))
|
||||
(when (:board-id rparams)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dwv/initialize-viewport))
|
||||
(rx/take 1)
|
||||
(rx/map zoom-to-frame)))
|
||||
|
||||
(when-let [component-id (some-> rparams :component-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwl/go-to-local-component :id component-id :update-layout? (:update-layout rparams)))))
|
||||
(when-let [comment-id (some-> rparams :comment-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwcm/navigate-to-comment-id comment-id))))
|
||||
|
||||
(when (:board-id rparams)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dwv/initialize-viewport))
|
||||
(rx/take 1)
|
||||
(rx/map zoom-to-frame)))
|
||||
(when render-wasm?
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [redo-changes]}]
|
||||
(let [added (->> redo-changes
|
||||
(filter #(= (:type %) :add-obj))
|
||||
(map :id))]
|
||||
(->> (rx/from added)
|
||||
(rx/map process-wasm-object)))))))
|
||||
|
||||
(when-let [comment-id (some-> rparams :comment-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwcm/navigate-to-comment-id comment-id))))
|
||||
(when render-wasm?
|
||||
(let [local-commits-s
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/filter #(and (= :local (:source %))
|
||||
(not (contains? (:tags %) :position-data))))
|
||||
(rx/filter (complement empty?)))
|
||||
|
||||
(when render-wasm?
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [redo-changes]}]
|
||||
(let [added (->> redo-changes
|
||||
(filter #(= (:type %) :add-obj))
|
||||
(map :id))]
|
||||
(->> (rx/from added)
|
||||
(rx/map process-wasm-object)))))))
|
||||
notifier-s
|
||||
(rx/merge
|
||||
(->> local-commits-s (rx/debounce 1000))
|
||||
(->> stream (rx/filter dps/force-persist?)))
|
||||
|
||||
(when render-wasm?
|
||||
(let [local-commits-s
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/filter #(and (= :local (:source %))
|
||||
(not (contains? (:tags %) :position-data))))
|
||||
(rx/filter (complement empty?)))
|
||||
objects-s
|
||||
(rx/from-atom refs/workspace-page-objects {:emit-current-value? true})
|
||||
|
||||
notifier-s
|
||||
(rx/merge
|
||||
(->> local-commits-s (rx/debounce 1000))
|
||||
(->> stream (rx/filter dps/force-persist?)))
|
||||
current-page-id-s
|
||||
(rx/from-atom refs/current-page-id {:emit-current-value? true})]
|
||||
|
||||
objects-s
|
||||
(rx/from-atom refs/workspace-page-objects {:emit-current-value? true})
|
||||
(->> local-commits-s
|
||||
(rx/buffer-until notifier-s)
|
||||
(rx/with-latest-from objects-s)
|
||||
(rx/map
|
||||
(fn [[commits objects]]
|
||||
(->> commits
|
||||
(mapcat :redo-changes)
|
||||
(filter #(contains? #{:mod-obj :add-obj} (:type %)))
|
||||
(filter #(cfh/text-shape? objects (:id %)))
|
||||
(map #(vector
|
||||
(:id %)
|
||||
(wasm.api/calculate-position-data (get objects (:id %))))))))
|
||||
|
||||
current-page-id-s
|
||||
(rx/from-atom refs/current-page-id {:emit-current-value? true})]
|
||||
(rx/with-latest-from current-page-id-s)
|
||||
(rx/map
|
||||
(fn [[text-position-data page-id]]
|
||||
(let [changes
|
||||
(->> text-position-data
|
||||
(mapv (fn [[id position-data]]
|
||||
{:type :mod-obj
|
||||
:id id
|
||||
:page-id page-id
|
||||
:operations
|
||||
[{:type :set
|
||||
:attr :position-data
|
||||
:val position-data
|
||||
:ignore-touched true
|
||||
:ignore-geometry true}]})))]
|
||||
(when (d/not-empty? changes)
|
||||
(dch/commit-changes
|
||||
{:redo-changes changes :undo-changes []
|
||||
:save-undo? false
|
||||
:tags #{:position-data}})))))
|
||||
(rx/take-until stoper-s))))
|
||||
|
||||
(->> local-commits-s
|
||||
(rx/buffer-until notifier-s)
|
||||
(rx/with-latest-from objects-s)
|
||||
(rx/map
|
||||
(fn [[commits objects]]
|
||||
(->> commits
|
||||
(mapcat :redo-changes)
|
||||
(filter #(contains? #{:mod-obj :add-obj} (:type %)))
|
||||
(filter #(cfh/text-shape? objects (:id %)))
|
||||
(map #(vector
|
||||
(:id %)
|
||||
(wasm.api/calculate-position-data (get objects (:id %))))))))
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo?]}]
|
||||
(if (and save-undo? (seq undo-changes))
|
||||
(let [entry {:undo-changes undo-changes
|
||||
:redo-changes redo-changes
|
||||
:undo-group undo-group
|
||||
:tags tags}]
|
||||
(rx/of (dwu/append-undo entry stack-undo?)))
|
||||
(rx/empty))))))
|
||||
(rx/take-until stoper-s))
|
||||
|
||||
(rx/with-latest-from current-page-id-s)
|
||||
(rx/map
|
||||
(fn [[text-position-data page-id]]
|
||||
(let [changes
|
||||
(->> text-position-data
|
||||
(mapv (fn [[id position-data]]
|
||||
{:type :mod-obj
|
||||
:id id
|
||||
:page-id page-id
|
||||
:operations
|
||||
[{:type :set
|
||||
:attr :position-data
|
||||
:val position-data
|
||||
:ignore-touched true
|
||||
:ignore-geometry true}]})))]
|
||||
(when (d/not-empty? changes)
|
||||
(dch/commit-changes
|
||||
{:redo-changes changes :undo-changes []
|
||||
:save-undo? false
|
||||
:tags #{:position-data}})))))
|
||||
(rx/take-until stoper-s))))
|
||||
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo?]}]
|
||||
(if (and save-undo? (seq undo-changes))
|
||||
(let [entry {:undo-changes undo-changes
|
||||
:redo-changes redo-changes
|
||||
:undo-group undo-group
|
||||
:tags tags}]
|
||||
(rx/of (dwu/append-undo entry stack-undo?)))
|
||||
(rx/empty))))))
|
||||
(rx/take-until stoper-s))))
|
||||
(rx/of (mcp/notify-other-tabs-disconnect)))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
(ptk/reify ::set-mcp-active
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-local :mcp :active] value))))
|
||||
(assoc-in state [:mcp :active] value))))
|
||||
|
||||
(defn start-reconnect-watcher!
|
||||
[]
|
||||
@ -61,7 +61,7 @@
|
||||
(fn []
|
||||
;; Try to reconnect if active and not connected
|
||||
(when-not (contains? #{"connecting" "connected"}
|
||||
(-> @st/state :workspace-local :mcp :connection))
|
||||
(-> @st/state :mcp :connection-status))
|
||||
(.log js/console "Reconnecting to MCP...")
|
||||
(st/emit! (ptk/data-event ::connect))))))))
|
||||
|
||||
@ -72,55 +72,46 @@
|
||||
(rx/dispose! @interval-sub)
|
||||
(reset! interval-sub nil)))
|
||||
|
||||
;; This event will arrive when the user selects disconnect on the menu
|
||||
;; or there is a broadcast message for disconnection
|
||||
(defn user-disconnect-mcp
|
||||
[]
|
||||
(ptk/reify ::remote-disconnect-mcp
|
||||
(declare manage-mcp-notification)
|
||||
|
||||
(defn handle-pong
|
||||
[{:keys [id data]}]
|
||||
(ptk/reify ::handle-pong
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [mcp-state (get state :mcp)]
|
||||
(cond
|
||||
(= "connected" (:connection-status data))
|
||||
(update state :mcp assoc :connected-tab id)
|
||||
|
||||
(and (= "disconnected" (:connection-status data))
|
||||
(= id (:connection-status mcp-state)))
|
||||
(update state :mcp dissoc :connected-tab)
|
||||
|
||||
:else
|
||||
state)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (ptk/data-event ::disconnect)))
|
||||
(rx/of (manage-mcp-notification)))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(stop-reconnect-watcher!))))
|
||||
|
||||
(defn connect-mcp
|
||||
;; This event will arrive when a new workspace is open in another tab
|
||||
(defn handle-ping
|
||||
[]
|
||||
(ptk/reify ::connect-mcp
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ stream]
|
||||
(mbc/emit! :mcp-enabled-change-connection false)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::disconnect))
|
||||
(rx/take 1)
|
||||
(rx/map #(ptk/data-event ::connect))))))
|
||||
|
||||
(defn manage-mcp-notification
|
||||
[]
|
||||
(ptk/reify ::manage-mcp-notification
|
||||
(ptk/reify ::handle-ping
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [mcp-connected? (= "connected" (-> state :workspace-local :mcp :connection))
|
||||
mcp-enabled? (true? (-> state :profile :props :mcp-enabled))
|
||||
num-sessions (-> state :workspace-presence count)
|
||||
multi-session? (> num-sessions 1)
|
||||
mcp-active? (-> state :workspace-local :mcp :active)]
|
||||
(if (and mcp-enabled? multi-session?)
|
||||
(if (or mcp-connected? mcp-active?)
|
||||
(rx/of (ntf/hide))
|
||||
(rx/of (ntf/dialog :content (tr "notifications.mcp.active-in-another-tab")
|
||||
:cancel {:label (tr "labels.dismiss")
|
||||
:callback #(st/emit! (ntf/hide)
|
||||
(ptk/event ::ev/event {::ev/name "confirm-mcp-tab-switch"
|
||||
::ev/origin "workspace-notification"}))}
|
||||
:accept {:label (tr "labels.switch")
|
||||
:callback #(st/emit! (connect-mcp)
|
||||
(ptk/event ::ev/event {::ev/name "dismiss-mcp-tab-switch"
|
||||
::ev/origin "workspace-notification"}))})))
|
||||
(rx/of (ntf/hide)))))))
|
||||
(let [conn-status (get-in state [:mcp :connection-status])]
|
||||
(rx/of (mbc/event :mcp/pong {:connection-status conn-status}))))))
|
||||
|
||||
;; This event will arrive when the mcp is enabled in the main menu
|
||||
(defn notify-other-tabs-disconnect
|
||||
[]
|
||||
(ptk/reify ::notify-other-tabs-disconnect
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (mbc/event :mcp/pong {:connection-status "disconnected"})))))
|
||||
|
||||
;; This event will arrive when the mcp is enabled in the dashboard
|
||||
(defn update-mcp-status
|
||||
[value]
|
||||
(ptk/reify ::update-mcp-status
|
||||
@ -131,27 +122,81 @@
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/merge
|
||||
(rx/of (manage-mcp-notification)))
|
||||
(case value
|
||||
true (rx/of (ptk/data-event ::connect))
|
||||
false (rx/of (ptk/data-event ::disconnect))
|
||||
nil))))
|
||||
(rx/of (manage-mcp-notification))
|
||||
(case value
|
||||
true (rx/of (ptk/data-event ::connect))
|
||||
false (rx/of (ptk/data-event ::disconnect))
|
||||
nil)))))
|
||||
|
||||
(defn update-mcp-connection
|
||||
(defn update-mcp-connection-status
|
||||
[value]
|
||||
(ptk/reify ::update-mcp-plugin-connection
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:workspace-local :mcp] assoc :connection value))
|
||||
(update state :mcp assoc :connection-status value))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (manage-mcp-notification)))))
|
||||
(rx/of (manage-mcp-notification)
|
||||
(mbc/event :mcp/pong {:connection-status value})))))
|
||||
|
||||
(defn init-mcp!
|
||||
(defn connect-mcp
|
||||
[]
|
||||
(ptk/reify ::connect-mcp
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :mcp assoc :connected-tab (:session-id state)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (mbc/event :mcp/force-disconect {})
|
||||
(ptk/data-event ::connect)))))
|
||||
|
||||
;; This event will arrive when the user selects disconnect on the menu
|
||||
;; or there is a broadcast message for disconnection
|
||||
(defn user-disconnect-mcp
|
||||
[]
|
||||
(ptk/reify ::user-disconnect-mcp
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (ptk/data-event ::disconnect)
|
||||
(update-mcp-connection-status "disconnected")))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(stop-reconnect-watcher!))))
|
||||
|
||||
(defn- manage-mcp-notification
|
||||
[]
|
||||
(ptk/reify ::manage-mcp-notification
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [mcp-state (get state :mcp)
|
||||
|
||||
mcp-enabled? (-> state :profile :props :mcp-enabled)
|
||||
|
||||
current-tab-id (get state :session-id)
|
||||
connected-tab-id (get mcp-state :connected-tab)]
|
||||
|
||||
(if mcp-enabled?
|
||||
(if (= connected-tab-id current-tab-id)
|
||||
(rx/of (ntf/hide))
|
||||
(rx/of (ntf/dialog
|
||||
{:content (tr "notifications.mcp.active-in-another-tab")
|
||||
:cancel {:label (tr "labels.dismiss")
|
||||
:callback #(st/emit! (ntf/hide)
|
||||
(ev/event {::ev/name "confirm-mcp-tab-switch"
|
||||
::ev/origin "workspace-notification"}))}
|
||||
:accept {:label (tr "labels.switch")
|
||||
:callback #(st/emit! (connect-mcp)
|
||||
(ev/event {::ev/name "dismiss-mcp-tab-switch"
|
||||
::ev/origin "workspace-notification"}))}})))
|
||||
(rx/of (ntf/hide)))))))
|
||||
|
||||
(defn init-mcp
|
||||
[stream]
|
||||
(->> (rp/cmd! :get-current-mcp-token)
|
||||
(rx/subs!
|
||||
(rx/tap
|
||||
(fn [{:keys [token]}]
|
||||
(when token
|
||||
(dp/start-plugin!
|
||||
@ -168,7 +213,7 @@
|
||||
(fn [status]
|
||||
(when (= status "connected")
|
||||
(start-reconnect-watcher!))
|
||||
(st/emit! (update-mcp-connection status))
|
||||
(st/emit! (update-mcp-connection-status status))
|
||||
(log/info :hint "MCP STATUS" :status status))
|
||||
|
||||
:on
|
||||
@ -183,13 +228,65 @@
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? event))
|
||||
(rx/take-until stopper)
|
||||
(rx/subs! #(cb))))))}}))))))
|
||||
(rx/subs! #(cb))))))}}))))
|
||||
(rx/ignore)))
|
||||
|
||||
(defn init-mcp-connection
|
||||
(defn init
|
||||
[]
|
||||
(ptk/reify ::init-mcp-connection
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream]
|
||||
(when (and (contains? cf/flags :mcp)
|
||||
(-> state :profile :props :mcp-enabled))
|
||||
(init-mcp! stream)))))
|
||||
(ptk/reify ::init
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :mcp assoc :connected-tab (:session-id state) :active true))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [stoper-s (rx/merge
|
||||
(rx/filter (ptk/type? :app.main.data.workspace/finalize-workspace) stream)
|
||||
(rx/filter (ptk/type? ::init) stream))
|
||||
session-id (get state :session-id)
|
||||
enabled? (-> state :profile :props :mcp-enabled)]
|
||||
|
||||
(->> (rx/merge
|
||||
(if enabled?
|
||||
(rx/merge
|
||||
(init-mcp stream)
|
||||
|
||||
(rx/of (mbc/event :mcp/ping {}))
|
||||
|
||||
(->> mbc/stream
|
||||
(rx/filter (mbc/type? :mcp/ping))
|
||||
(rx/filter (fn [{:keys [id]}]
|
||||
(not= session-id id)))
|
||||
(rx/map handle-ping))
|
||||
|
||||
(->> mbc/stream
|
||||
(rx/filter (mbc/type? :mcp/pong))
|
||||
(rx/filter (fn [{:keys [id]}]
|
||||
(not= session-id id)))
|
||||
(rx/map handle-pong))
|
||||
|
||||
(->> mbc/stream
|
||||
(rx/filter (mbc/type? :mcp/force-disconect))
|
||||
(rx/filter (fn [{:keys [id]}]
|
||||
(not= session-id id)))
|
||||
(rx/map deref)
|
||||
(rx/map (fn [] (user-disconnect-mcp)))))
|
||||
(rx/empty))
|
||||
|
||||
(->> mbc/stream
|
||||
(rx/filter (mbc/type? :mcp/enable))
|
||||
(rx/mapcat (fn [_]
|
||||
;; NOTE: we don't need an explicit
|
||||
;; connect because the plugin has
|
||||
;; auto-connect
|
||||
(rx/of (update-mcp-status true)
|
||||
(init)))))
|
||||
|
||||
(->> mbc/stream
|
||||
(rx/filter (mbc/type? :mcp/disable))
|
||||
(rx/mapcat (fn [_]
|
||||
(rx/of (update-mcp-status false)
|
||||
(init)
|
||||
(user-disconnect-mcp))))))
|
||||
|
||||
(rx/take-until stoper-s))))))
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.common.time :as ct]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.common :as dc]
|
||||
[app.main.data.helpers :as dsh]
|
||||
@ -24,7 +23,6 @@
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
[app.main.data.workspace.layout :as dwly]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.mcp :as mcp]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.router :as rt]
|
||||
[app.util.globals :refer [global]]
|
||||
@ -214,12 +212,8 @@
|
||||
(update [_ state]
|
||||
(if (or (= :disconnect type) (= :leave-file type))
|
||||
(update state :workspace-presence dissoc session-id)
|
||||
(update state :workspace-presence update-presence)))
|
||||
(update state :workspace-presence update-presence))))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(when (contains? cf/flags :mcp)
|
||||
(rx/of (mcp/manage-mcp-notification)))))))
|
||||
|
||||
(defn handle-pointer-update
|
||||
[{:keys [page-id session-id position zoom zoom-inverse vbox vport] :as msg}]
|
||||
|
||||
@ -150,6 +150,9 @@
|
||||
(def workspace-global
|
||||
(l/derived :workspace-global st/state))
|
||||
|
||||
(def mcp
|
||||
(l/derived :mcp st/state))
|
||||
|
||||
(def workspace-drawing
|
||||
(l/derived :workspace-drawing st/state))
|
||||
|
||||
|
||||
@ -285,8 +285,8 @@
|
||||
::ev/origin "integrations"})
|
||||
(ev/event {::ev/name "enable-mcp"
|
||||
::ev/origin "integrations"
|
||||
:source "key-creation"}))
|
||||
(mbc/emit! :mcp-enabled-change-status true)
|
||||
:source "key-creation"})
|
||||
(mbc/event :mcp/enable {}))
|
||||
(reset! created? true)))]
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
@ -326,8 +326,8 @@
|
||||
(st/emit! (du/delete-access-token {:id mcp-key-id})
|
||||
(du/update-profile-props {:mcp-enabled true})
|
||||
(ev/event {::ev/name "regenerate-mcp-key"
|
||||
::ev/origin "integrations"}))
|
||||
(mbc/emit! :mcp-enabled-change-status true)
|
||||
::ev/origin "integrations"})
|
||||
(mbc/event :mcp/enable {}))
|
||||
(reset! created? true)))]
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
@ -437,8 +437,10 @@
|
||||
:timeout notification-timeout})
|
||||
(ev/event {::ev/name (if (true? value) "enable-mcp" "disable-mcp")
|
||||
::ev/origin "integrations"
|
||||
:source "toggle"}))
|
||||
(mbc/emit! :mcp-enabled-change-status value)))
|
||||
:source "toggle"})
|
||||
(if value
|
||||
(mbc/event :mcp/enable {})
|
||||
(mbc/event :mcp/disable {})))))
|
||||
|
||||
handle-generate-mcp-key
|
||||
(mf/use-fn
|
||||
@ -455,8 +457,8 @@
|
||||
(let [params {:id (:id mcp-key)}
|
||||
mdata {:on-success #(st/emit! (du/fetch-access-tokens))}]
|
||||
(st/emit! (du/delete-access-token (with-meta params mdata))
|
||||
(du/update-profile-props {:mcp-enabled false}))
|
||||
(mbc/emit! :mcp-enabled-change-status false))))
|
||||
(du/update-profile-props {:mcp-enabled false})
|
||||
(mbc/event :mcp/disable {})))))
|
||||
|
||||
on-copy-to-clipboard
|
||||
(mf/use-fn
|
||||
|
||||
@ -752,10 +752,10 @@
|
||||
(let [plugins? (features/active-feature? @st/state "plugins/runtime")
|
||||
|
||||
profile (mf/deref refs/profile)
|
||||
workspace-local (mf/deref refs/workspace-local)
|
||||
mcp (mf/deref refs/mcp)
|
||||
|
||||
mcp-enabled? (true? (-> profile :props :mcp-enabled))
|
||||
mcp-connected? (= "connected" (-> workspace-local :mcp :connection))
|
||||
mcp-connected? (= "connected" (get mcp :connection-status))
|
||||
|
||||
on-nav-to-integrations
|
||||
(mf/use-fn
|
||||
@ -815,8 +815,8 @@
|
||||
|
||||
(mf/defc menu*
|
||||
[{:keys [layout file]}]
|
||||
(let [profile (mf/deref refs/profile)
|
||||
workspace-local (mf/deref refs/workspace-local)
|
||||
(let [profile (mf/deref refs/profile)
|
||||
mcp (mf/deref refs/mcp)
|
||||
|
||||
show-menu* (mf/use-state false)
|
||||
show-menu? (deref show-menu*)
|
||||
@ -990,7 +990,7 @@
|
||||
(> (ct/now)))
|
||||
|
||||
mcp-enabled? (true? (-> profile :props :mcp-enabled))
|
||||
mcp-connection (-> workspace-local :mcp :connection)
|
||||
mcp-connection (get mcp :connection-status)
|
||||
mcp-connected? (= mcp-connection "connected")
|
||||
mcp-error? (= mcp-connection "error")
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user