✨ Move the dashboard MCP menu to the page headers

Show the MCP menu next to the layout toggle in the header of the
projects, files and deleted pages, instead of in the sidebar. It
uses the secondary button style there and a visible dot colour
while disconnected.

Clicking the MCP button now toggles the menu instead of only
opening it. Dashboard events report "dashboard:header" as their
origin.
This commit is contained in:
alonso.torres 2026-10-07 13:23:29 +02:00
parent 036f59266b
commit c4d2ca9cd1
7 changed files with 31 additions and 25 deletions

View File

@ -81,7 +81,7 @@ Some important concepts for users:
Connect the tabs you want to use:
* **Remote MCP**: choose **MCP → Connect** in the dashboard sidebar or a file's toolbar. Use **Disconnect** in the same place to disconnect that tab.
* **Remote MCP**: choose **MCP → Connect** in the dashboard header or a file's toolbar. Use **Disconnect** in the same place to disconnect that tab.
* **Local MCP**: run the plugin in each file and connect it to your local server. Each plugin connection has its own session ID.
* Connecting or disconnecting one tab does not disconnect the others.
* A connected tab stays connected while navigating between the dashboard and files. Its session ID stays the same; requests operate on the currently open file.

View File

@ -7,9 +7,11 @@
(ns app.main.ui.components.mcp-menu
(:require-macros [app.main.style :as stl])
(:require
[app.config :as cf]
[app.main.data.event :as ev]
[app.main.data.notifications :as ntf]
[app.main.data.workspace.mcp :as mcp]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown-menu :refer [dropdown-menu* dropdown-menu-item*]]
[app.main.ui.ds.buttons.button :refer [button*]]
@ -36,13 +38,13 @@
open-timer* (mf/use-ref nil)
close-timer* (mf/use-ref nil)
on-open-menu
on-toggle-menu
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(cancel-timer! open-timer*)
(cancel-timer! close-timer*)
(reset! menu-open* true)))
(swap! menu-open* not)))
on-close-menu
(mf/use-fn
@ -80,7 +82,7 @@
(mf/deps dashboard)
#(st/emit! (mcp/connect-mcp)
(ev/event {::ev/name "connect-mcp-plugin"
::ev/origin (if dashboard "dashboard:sidebar" "workspace:toolbar")})))
::ev/origin (if dashboard "dashboard:header" "workspace:toolbar")})))
on-disconnect
(mf/use-fn
@ -102,8 +104,8 @@
[:div {:class (stl/css-case :mcp-tool true :dashboard dashboard)
:on-pointer-enter on-display-menu
:on-pointer-leave on-hide-menu}
[:> button* {:variant "ghost"
:on-click on-open-menu
[:> button* {:variant (if dashboard "secondary" "ghost")
:on-click on-toggle-menu
:aria-haspopup true
:aria-expanded menu-open?
:aria-pressed menu-open?
@ -115,9 +117,7 @@
:connected is-mcp-connected)}]
[:span {:class (stl/css-case :toolbar-mcp-button-label true
:connected is-mcp-connected)}
(if (and dashboard is-mcp-connected)
(tr "workspace.toolbar.mcp-connected")
(tr "workspace.toolbar.mcp"))]]]
(tr "workspace.toolbar.mcp")]]]
[:div {:class (stl/css :toolbar-mcp-menu)}
[:> dropdown-menu* {:show menu-open?
@ -139,3 +139,14 @@
(if is-connection-requested
(tr "workspace.header.menu.mcp.plugin.status.disconnect")
(tr "workspace.header.menu.mcp.plugin.status.connect"))]]]]))
(mf/defc dashboard-mcp-menu*
[]
(let [mcp-state (mf/deref refs/mcp)]
(when (and (contains? cf/flags :mcp)
(:enabled mcp-state)
(:token-valid mcp-state))
[:> mcp-menu* {:dashboard true
:is-mcp-connected (= "connected" (:connection-status mcp-state))
:is-connection-requested (:connection-requested mcp-state)
:session-id (:session-id mcp-state)}])))

View File

@ -13,10 +13,10 @@
.mcp-tool {
position: relative;
}
.dashboard {
margin-inline: var(--sp-l);
&.dashboard {
--mcp-disconnected-dot-color: var(--color-foreground-secondary);
}
}
.toolbar-mcp-button {
@ -38,7 +38,7 @@
inline-size: px2rem(6);
block-size: px2rem(6);
border-radius: 50%;
background-color: var(--color-background-disabled);
background-color: var(--mcp-disconnected-dot-color, var(--color-background-disabled));
&.connected {
background-color: var(--color-accent-primary);
@ -72,6 +72,7 @@
.toolbar-mcp-menu {
position: absolute;
z-index: var(--z-index-dropdown);
left: 0;
top: $sz-36;
}
@ -85,7 +86,6 @@
display: flex;
flex-direction: column;
gap: var(--sp-xs);
z-index: var(--z-index-dropdown);
margin: 0;
padding: var(--sp-xs);
border: $b-2 solid var(--panel-border-color);

View File

@ -16,6 +16,7 @@
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.context-menu-a11y :refer [context-menu*]]
[app.main.ui.components.mcp-menu :refer [dashboard-mcp-menu*]]
[app.main.ui.dashboard.grid :refer [grid*]]
[app.main.ui.dashboard.layout-toggle :as lt :refer [layout-toggle*]]
[app.main.ui.dashboard.subscription :refer [get-subscription-type]]
@ -64,6 +65,7 @@
[:div#dashboard-deleted-title {:class (stl/css :dashboard-title)}
[:h1 (tr "dashboard.projects-title")]]
[:div {:class (stl/css :dashboard-header-actions)}
[:> dashboard-mcp-menu*]
[:> layout-toggle* {:layout layout :on-change on-change}]]])
(mf/defc project-context-menu*

View File

@ -14,6 +14,7 @@
[app.main.data.project :as dpj]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.mcp-menu :refer [dashboard-mcp-menu*]]
[app.main.ui.dashboard.grid :refer [grid*]]
[app.main.ui.dashboard.import :as udi]
[app.main.ui.dashboard.inline-edition :refer [inline-edition]]
@ -102,6 +103,7 @@
(:name project)]]))
[:div {:class (stl/css :dashboard-header-actions)}
[:> dashboard-mcp-menu*]
[:> layout-toggle* {:layout layout
:on-change on-change}]

View File

@ -17,6 +17,7 @@
[app.main.data.team :as dtm]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.mcp-menu :refer [dashboard-mcp-menu*]]
[app.main.ui.dashboard.deleted :as deleted]
[app.main.ui.dashboard.grid :refer [line-grid*]]
[app.main.ui.dashboard.import :as udi]
@ -50,6 +51,7 @@
(tr "dashboard.personal-projects")
(tr "dashboard.projects-title"))]]
[:div {:class (stl/css :dashboard-header-actions)}
[:> dashboard-mcp-menu*]
[:> layout-toggle* {:layout layout
:on-change on-change}]
(when can-edit

View File

@ -23,7 +23,6 @@
[app.main.ui.components.dropdown-menu :refer [dropdown-menu*
dropdown-menu-item*]]
[app.main.ui.components.link :refer [link*]]
[app.main.ui.components.mcp-menu :refer [mcp-menu*]]
[app.main.ui.dashboard.check-updates :as dcu]
[app.main.ui.dashboard.comments :refer [comments-icon* comments-section]]
[app.main.ui.dashboard.inline-edition :refer [inline-edition]]
@ -256,7 +255,6 @@
(get default-project :id)
team-id (get team :id)
mcp-state (mf/deref refs/mcp)
projects? (= section :dashboard-recent)
fonts? (= section :dashboard-fonts)
@ -384,15 +382,6 @@
[:span {:class (stl/css :element-title)} (tr "labels.shared-libraries")]]]]]
(when (and (contains? cf/flags :mcp)
(:enabled mcp-state)
(:token-valid mcp-state))
[:div {:class (stl/css :sidebar-content-section)}
[:> mcp-menu* {:dashboard true
:is-mcp-connected (= "connected" (:connection-status mcp-state))
:is-connection-requested (:connection-requested mcp-state)
:session-id (:session-id mcp-state)}]])
[:div {:class (stl/css :sidebar-content-section)
:data-testid "pinned-projects"}
[:div {:class (stl/css :sidebar-section-title)}