mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 05:50:35 +00:00
✨ Share the MCP connection menu with the dashboard
Connection status: - Replace the dashboard's action-only link with an explicit connected label and indicator, using the theme's existing accent colour. - Reflect the actual connection state independently of connection intent. Shared controls: - Extract the toolbar's MCP menu and styles into a shared component used by both the dashboard and workspace. - Provide connection status, session ID, copy, connect and disconnect actions in both locations, preserving hover and keyboard behaviour. - Keep dashboard and toolbar event origins distinct and update the dashboard connection instructions. Validation: - Confirm dashboard disconnect/reconnect, menu opening, keyboard navigation and Escape dismissal in the running app. - Pass live compilation, targeted ClojureScript and SCSS lint, formatting checks and git diff checks. AI-assisted-by: gpt-6 Signed-off-by: Dominik Jain <dominik.jain@oraios-ai.de>
This commit is contained in:
parent
be17fabc6a
commit
54cce66c13
@ -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 **MCP → Connect** in a file's toolbar. Use **Disconnect** in the same place to disconnect that tab.
|
||||
* **Remote MCP**: choose **MCP → Connect** in the dashboard sidebar 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.
|
||||
|
||||
141
frontend/src/app/main/ui/components/mcp_menu.cljs
Normal file
141
frontend/src/app/main/ui/components/mcp_menu.cljs
Normal file
@ -0,0 +1,141 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS SUBSIDIARY SL
|
||||
|
||||
(ns app.main.ui.components.mcp-menu
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.workspace.mcp :as mcp]
|
||||
[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*]]
|
||||
[app.util.clipboard :as clipboard]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.timers :as ts]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- cancel-timer!
|
||||
[timer-ref*]
|
||||
(when-let [timer (mf/ref-val timer-ref*)]
|
||||
(ts/dispose! timer)
|
||||
(mf/set-ref-val! timer-ref* nil)))
|
||||
|
||||
(mf/defc mcp-menu*
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [is-mcp-connected is-connection-requested session-id dashboard]}]
|
||||
(let [copied-text (tr "workspace.toolbar.mcp-session-copied")
|
||||
copy-error (tr "errors.clipboard-api-unavailable")
|
||||
menu-open* (mf/use-state false)
|
||||
menu-open? (deref menu-open*)
|
||||
|
||||
open-timer* (mf/use-ref nil)
|
||||
close-timer* (mf/use-ref nil)
|
||||
|
||||
on-open-menu
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(cancel-timer! open-timer*)
|
||||
(cancel-timer! close-timer*)
|
||||
(reset! menu-open* true)))
|
||||
|
||||
on-close-menu
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(cancel-timer! open-timer*)
|
||||
(cancel-timer! close-timer*)
|
||||
(reset! menu-open* false)))
|
||||
|
||||
on-display-menu
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(cancel-timer! close-timer*)
|
||||
(cancel-timer! open-timer*)
|
||||
(mf/set-ref-val!
|
||||
open-timer*
|
||||
(ts/schedule 350
|
||||
#(do
|
||||
(reset! menu-open* true)
|
||||
(mf/set-ref-val! open-timer* nil))))))
|
||||
|
||||
on-hide-menu
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(cancel-timer! open-timer*)
|
||||
(cancel-timer! close-timer*)
|
||||
(mf/set-ref-val!
|
||||
close-timer*
|
||||
(ts/schedule 350
|
||||
#(do
|
||||
(reset! menu-open* false)
|
||||
(mf/set-ref-val! close-timer* nil))))))
|
||||
|
||||
on-connect
|
||||
(mf/use-fn
|
||||
(mf/deps dashboard)
|
||||
#(st/emit! (mcp/connect-mcp)
|
||||
(ev/event {::ev/name "connect-mcp-plugin"
|
||||
::ev/origin (if dashboard "dashboard:sidebar" "workspace:toolbar")})))
|
||||
|
||||
on-disconnect
|
||||
(mf/use-fn
|
||||
#(st/emit! (mcp/user-disconnect-mcp)))
|
||||
|
||||
on-copy-session
|
||||
(mf/use-fn
|
||||
(mf/deps session-id copied-text copy-error)
|
||||
(fn []
|
||||
(-> (clipboard/to-clipboard session-id)
|
||||
(.then #(st/emit! (ntf/info copied-text)))
|
||||
(.catch #(st/emit! (ntf/error copy-error))))))]
|
||||
|
||||
(mf/with-effect []
|
||||
(fn []
|
||||
(cancel-timer! open-timer*)
|
||||
(cancel-timer! close-timer*)))
|
||||
|
||||
[: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
|
||||
:aria-haspopup true
|
||||
:aria-expanded menu-open?
|
||||
:aria-pressed menu-open?
|
||||
:data-tool "mcp"
|
||||
:data-testid "mcp-btn"}
|
||||
[:div {:class (stl/css-case :toolbar-mcp-button true
|
||||
:selected menu-open?)}
|
||||
[:span {:class (stl/css-case :toolbar-mcp-button-dot true
|
||||
: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"))]]]
|
||||
|
||||
[:div {:class (stl/css :toolbar-mcp-menu)}
|
||||
[:> dropdown-menu* {:show menu-open?
|
||||
:on-close on-close-menu
|
||||
:class (stl/css :toolbar-mcp-dropdown)}
|
||||
(when (or is-mcp-connected session-id)
|
||||
[:li {:class (stl/css :toolbar-mcp-dropdown-info)
|
||||
:role "presentation"}
|
||||
(when is-mcp-connected
|
||||
[:span (tr "workspace.toolbar.mcp-connected")])
|
||||
(when session-id
|
||||
[:span (tr "workspace.toolbar.mcp-session-id" session-id)])])
|
||||
(when session-id
|
||||
[:> dropdown-menu-item* {:class (stl/css :toolbar-mcp-dropdown-item)
|
||||
:on-click on-copy-session}
|
||||
(tr "workspace.toolbar.mcp-copy-session-id")])
|
||||
[:> dropdown-menu-item* {:class (stl/css :toolbar-mcp-dropdown-item)
|
||||
:on-click (if is-connection-requested on-disconnect on-connect)}
|
||||
(if is-connection-requested
|
||||
(tr "workspace.header.menu.mcp.plugin.status.disconnect")
|
||||
(tr "workspace.header.menu.mcp.plugin.status.connect"))]]]]))
|
||||
127
frontend/src/app/main/ui/components/mcp_menu.scss
Normal file
127
frontend/src/app/main/ui/components/mcp_menu.scss
Normal file
@ -0,0 +1,127 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) KALEIDOS SUBSIDIARY SL
|
||||
|
||||
@use "ds/_borders.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/_utils.scss" as *;
|
||||
@use "ds/spacing.scss" as *;
|
||||
@use "ds/typography.scss" as t;
|
||||
@use "ds/z-index.scss" as *;
|
||||
|
||||
.mcp-tool {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
margin-inline: var(--sp-l);
|
||||
}
|
||||
|
||||
.toolbar-mcp-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
.toolbar-mcp-button-label {
|
||||
&.connected {
|
||||
color: var(--color-accent-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-mcp-button-dot {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
inline-size: px2rem(6);
|
||||
block-size: px2rem(6);
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-background-disabled);
|
||||
|
||||
&.connected {
|
||||
background-color: var(--color-accent-primary);
|
||||
}
|
||||
|
||||
// One-shot "blob" ripple that confirms the moment the tab becomes
|
||||
// connected (e.g. after using "Switch here"). Triggered purely by the
|
||||
// `.connected` class appearing, in sync with the color change.
|
||||
&.connected::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-accent-primary);
|
||||
opacity: 0;
|
||||
animation: mcp-status-blob 0.5s ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mcp-status-blob {
|
||||
from {
|
||||
opacity: 0.5;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scale(2.5);
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-mcp-menu {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: $sz-36;
|
||||
}
|
||||
|
||||
// Same look as the workspace main menu (`.base-menu` in main_menu.scss).
|
||||
// The toolbar overrides `--menu-background-color`, so restore the global
|
||||
// menu value here.
|
||||
.toolbar-mcp-dropdown {
|
||||
--menu-background-color: var(--color-background-tertiary);
|
||||
|
||||
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);
|
||||
border-radius: $br-8;
|
||||
background-color: var(--menu-background-color);
|
||||
box-shadow: 0 0 $sz-12 0 var(--menu-shadow-color);
|
||||
}
|
||||
|
||||
.toolbar-mcp-dropdown-info {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-xs);
|
||||
padding: var(--sp-s) var(--sp-m);
|
||||
border-radius: $br-8;
|
||||
color: var(--color-foreground-secondary);
|
||||
background-color: var(--color-background-primary);
|
||||
font-size: px2rem(11);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.toolbar-mcp-dropdown-item {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--sp-s) var(--sp-m);
|
||||
border-radius: $br-8;
|
||||
color: var(--menu-foreground-color);
|
||||
background-color: var(--menu-background-color);
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
--menu-foreground-color: var(--menu-foreground-color-hover);
|
||||
--menu-background-color: var(--menu-background-color-hover);
|
||||
}
|
||||
}
|
||||
@ -17,13 +17,13 @@
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.team :as dtm]
|
||||
[app.main.data.workspace.mcp :as mcp]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.router :as rt]
|
||||
[app.main.store :as st]
|
||||
[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]]
|
||||
@ -257,12 +257,6 @@
|
||||
|
||||
team-id (get team :id)
|
||||
mcp-state (mf/deref refs/mcp)
|
||||
mcp-requested? (:connection-requested mcp-state)
|
||||
toggle-mcp (mf/use-fn
|
||||
(mf/deps mcp-requested?)
|
||||
#(st/emit! (if mcp-requested?
|
||||
(mcp/user-disconnect-mcp)
|
||||
(mcp/connect-mcp))))
|
||||
|
||||
projects? (= section :dashboard-recent)
|
||||
fonts? (= section :dashboard-fonts)
|
||||
@ -394,16 +388,10 @@
|
||||
(:enabled mcp-state)
|
||||
(:token-valid mcp-state))
|
||||
[:div {:class (stl/css :sidebar-content-section)}
|
||||
[:ul {:class (stl/css :sidebar-nav)}
|
||||
[:li {:class (stl/css :sidebar-nav-item)}
|
||||
[:> link* {:action toggle-mcp
|
||||
:keyboard-action toggle-mcp
|
||||
:class (stl/css :sidebar-link)}
|
||||
[:span {:class (stl/css :element-title)}
|
||||
"MCP: "
|
||||
(if mcp-requested?
|
||||
(tr "workspace.header.menu.mcp.plugin.status.disconnect")
|
||||
(tr "workspace.header.menu.mcp.plugin.status.connect"))]]]]])
|
||||
[:> 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"}
|
||||
|
||||
@ -12,24 +12,20 @@
|
||||
[app.config :as cf]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.drawing.common :as dwdc]
|
||||
[app.main.data.workspace.mcp :as mcp]
|
||||
[app.main.data.workspace.media :as dwm]
|
||||
[app.main.data.workspace.path.state :as pst]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.features :as features]
|
||||
[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.components.file-uploader :as file-uploader]
|
||||
[app.main.ui.components.mcp-menu :refer [mcp-menu*]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.util.clipboard :as clipboard]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
@ -297,118 +293,6 @@
|
||||
:ref ref
|
||||
:on-selected on-selected}]]))
|
||||
|
||||
(mf/defc mcp-tool*
|
||||
{::mf/private true
|
||||
::mf/wrap [mf/memo]}
|
||||
[{:keys [is-mcp-connected is-connection-requested session-id]}]
|
||||
(let [copied-text (tr "workspace.toolbar.mcp-session-copied")
|
||||
copy-error (tr "errors.clipboard-api-unavailable")
|
||||
menu-open* (mf/use-state false)
|
||||
menu-open? (deref menu-open*)
|
||||
|
||||
open-timer* (mf/use-ref nil)
|
||||
close-timer* (mf/use-ref nil)
|
||||
|
||||
on-open-menu
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(cancel-timer! open-timer*)
|
||||
(cancel-timer! close-timer*)
|
||||
(reset! menu-open* true)))
|
||||
|
||||
on-close-menu
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(cancel-timer! open-timer*)
|
||||
(cancel-timer! close-timer*)
|
||||
(reset! menu-open* false)))
|
||||
|
||||
on-display-menu
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(cancel-timer! close-timer*)
|
||||
(cancel-timer! open-timer*)
|
||||
(mf/set-ref-val!
|
||||
open-timer*
|
||||
(ts/schedule 350
|
||||
#(do
|
||||
(reset! menu-open* true)
|
||||
(mf/set-ref-val! open-timer* nil))))))
|
||||
|
||||
on-hide-menu
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(cancel-timer! open-timer*)
|
||||
(cancel-timer! close-timer*)
|
||||
(mf/set-ref-val!
|
||||
close-timer*
|
||||
(ts/schedule 350
|
||||
#(do
|
||||
(reset! menu-open* false)
|
||||
(mf/set-ref-val! close-timer* nil))))))
|
||||
|
||||
on-connect
|
||||
(mf/use-fn
|
||||
#(st/emit! (mcp/connect-mcp)
|
||||
(ev/event {::ev/name "connect-mcp-plugin"
|
||||
::ev/origin "workspace:toolbar"})))
|
||||
|
||||
on-disconnect
|
||||
(mf/use-fn
|
||||
#(st/emit! (mcp/user-disconnect-mcp)))
|
||||
|
||||
on-copy-session
|
||||
(mf/use-fn
|
||||
(mf/deps session-id copied-text copy-error)
|
||||
(fn []
|
||||
(-> (clipboard/to-clipboard session-id)
|
||||
(.then #(st/emit! (ntf/info copied-text)))
|
||||
(.catch #(st/emit! (ntf/error copy-error))))))]
|
||||
|
||||
(mf/with-effect []
|
||||
(fn []
|
||||
(cancel-timer! open-timer*)
|
||||
(cancel-timer! close-timer*)))
|
||||
|
||||
[:div {:on-pointer-enter on-display-menu
|
||||
:on-pointer-leave on-hide-menu}
|
||||
[:> button* {:variant "ghost"
|
||||
:on-click on-open-menu
|
||||
:aria-haspopup true
|
||||
:aria-expanded menu-open?
|
||||
:aria-pressed menu-open?
|
||||
:data-tool "mcp"
|
||||
:data-testid "mcp-btn"}
|
||||
[:div {:class (stl/css-case :toolbar-mcp-button true
|
||||
:selected menu-open?)}
|
||||
[:span {:class (stl/css-case :toolbar-mcp-button-dot true
|
||||
:connected is-mcp-connected)}]
|
||||
[:span {:class (stl/css-case :toolbar-mcp-button-label true
|
||||
:connected is-mcp-connected)}
|
||||
(tr "workspace.toolbar.mcp")]]]
|
||||
|
||||
[:div {:class (stl/css :toolbar-mcp-menu)}
|
||||
[:> dropdown-menu* {:show menu-open?
|
||||
:on-close on-close-menu
|
||||
:class (stl/css :toolbar-mcp-dropdown)}
|
||||
(when (or is-mcp-connected session-id)
|
||||
[:li {:class (stl/css :toolbar-mcp-dropdown-info)
|
||||
:role "presentation"}
|
||||
(when is-mcp-connected
|
||||
[:span (tr "workspace.toolbar.mcp-connected")])
|
||||
(when session-id
|
||||
[:span (tr "workspace.toolbar.mcp-session-id" session-id)])])
|
||||
(when session-id
|
||||
[:> dropdown-menu-item* {:class (stl/css :toolbar-mcp-dropdown-item)
|
||||
:on-click on-copy-session}
|
||||
(tr "workspace.toolbar.mcp-copy-session-id")])
|
||||
[:> dropdown-menu-item* {:class (stl/css :toolbar-mcp-dropdown-item)
|
||||
:on-click (if is-connection-requested on-disconnect on-connect)}
|
||||
(if is-connection-requested
|
||||
(tr "workspace.header.menu.mcp.plugin.status.disconnect")
|
||||
(tr "workspace.header.menu.mcp.plugin.status.connect"))]]]]))
|
||||
|
||||
(mf/defc top-toolbar*
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [layout]}]
|
||||
@ -545,7 +429,7 @@
|
||||
|
||||
(when mcp-show?
|
||||
[:li {:class (stl/css :toolbar-option)}
|
||||
[:> mcp-tool* {:is-mcp-connected mcp-connected?
|
||||
[:> mcp-menu* {:is-mcp-connected mcp-connected?
|
||||
:is-connection-requested (:connection-requested mcp)
|
||||
:session-id (:session-id mcp)}]])]
|
||||
|
||||
|
||||
@ -101,112 +101,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-mcp-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
.toolbar-mcp-button-label {
|
||||
&.connected {
|
||||
color: var(--color-accent-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-mcp-button-dot {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
inline-size: px2rem(6);
|
||||
block-size: px2rem(6);
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-background-disabled);
|
||||
|
||||
&.connected {
|
||||
background-color: var(--color-accent-primary);
|
||||
}
|
||||
|
||||
// One-shot "blob" ripple that confirms the moment the tab becomes
|
||||
// connected (e.g. after using "Switch here"). Triggered purely by the
|
||||
// `.connected` class appearing, in sync with the color change.
|
||||
&.connected::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-accent-primary);
|
||||
opacity: 0;
|
||||
animation: mcp-status-blob 0.5s ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mcp-status-blob {
|
||||
from {
|
||||
opacity: 0.5;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scale(2.5);
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-mcp-menu {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: $sz-36;
|
||||
}
|
||||
|
||||
// Same look as the workspace main menu (`.base-menu` in main_menu.scss).
|
||||
// The toolbar overrides `--menu-background-color`, so restore the global
|
||||
// menu value here.
|
||||
.toolbar-mcp-dropdown {
|
||||
--menu-background-color: var(--color-background-tertiary);
|
||||
|
||||
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);
|
||||
border-radius: $br-8;
|
||||
background-color: var(--menu-background-color);
|
||||
box-shadow: 0 0 $sz-12 0 var(--menu-shadow-color);
|
||||
}
|
||||
|
||||
.toolbar-mcp-dropdown-info {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-xs);
|
||||
padding: var(--sp-s) var(--sp-m);
|
||||
border-radius: $br-8;
|
||||
color: var(--color-foreground-secondary);
|
||||
background-color: var(--color-background-primary);
|
||||
font-size: px2rem(11);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.toolbar-mcp-dropdown-item {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--sp-s) var(--sp-m);
|
||||
border-radius: $br-8;
|
||||
color: var(--menu-foreground-color);
|
||||
background-color: var(--menu-background-color);
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
--menu-foreground-color: var(--menu-foreground-color-hover);
|
||||
--menu-background-color: var(--menu-background-color-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-separator {
|
||||
--separator-color: var(--color-background-quaternary);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user