mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-10-05 07:02:29 +00:00
## WebUI:新增「插件」页
- 列表来自 on_disk(不是 loaded 集合)——**加载失败的插件也必须显示并带错误**,
否则一个语法错误看起来和"插件没装"完全一样
- 启用/禁用(PUT {"enabled":bool})、删除、编辑源码、安装/覆盖
- 显示 hook_errors:插件抛异常在别处毫无痕迹,没有这一栏的症状就是
"功能就是不work"
- 插到 dropzone 与代码编辑器都做了泛型化(bindDropzone / openCodeModal),
适配器与插件共用一份,而不是复制第二份只改 4 个 id 的函数
## TABS 收敛为单一常量
tab 清单原本是字面量散在三处:goTab、refresh()、admin-only 隐藏列表。
加一个 tab 意味着三处都要记得改,漏一处就是"路由认得但界面不显示"——
和今天早些时候 chain_step 漏报同一类静默缺口。现在只有 const TABS。
## Electron 壳:设置面板里的插件管理
渲染进程不能直连内嵌核心(没有 key、不知道端口),所以走 IPC:
renderer → plugins:proxy → main → HTTP /api/plugins
代理是 (method, path, body) 透传而不是固定命令表:固定表每加一个端点就要扩,
而"按钮存在但什么都不做"比"没有这个按钮"更糟。透传让渲染层能调用核心将来
新增的任何 /api/plugins 路由,路径在主进程校验。
## ★ GUI 此前零测试,而本次改动就引入了三类"看起来没事"的问题
1. 引用了不存在的 CSS 类(.tag / .sm)——渲染成无样式文本
2. 引用了不存在的 helper(esc / escAttr)——那是 WebUI 的,renderer/app.js
是独立文档,点击时 ReferenceError
3. .ghost/.primary 只在 .form .actions 作用域内生效,插件按钮在 .pl-acts 里
于是是无样式裸按钮
补 4 个静态判据(不启动 Electron,守卫的正是"打开应用才看得见"那一类):
TestGUICSSClassesExist 用到的类必须在样式表里定义
TestGUIHelperFunctionsAreDefined 被调用的函数必须有定义
TestGUIPluginPanelIsReachable 面板在 overlay 内、按钮已绑定、打开设置会加载
TestGUIIPCPathIsConstrained 代理必须限定 /api/plugins 前缀并拒绝路径穿越
写第一个判据时我错了三次:CSS 解析器先丢最后一个 selector、再把变量块当
selector、最后漏掉复合选择器(.tb-btn.tb-close)。两次"判据自己坏了"的
教训和本项目一贯一致——**判据出错的信号是它报了一个假问题**。现在改用宽松的
token 提取 + 显式的 guiKnownUnstyled 豁免表(blob/tgl/rail 是既有无样式类,
不是本次引入,失败它们只会让判据对新工作失去意义)。
## 变异验证
改坏唯一的 CSS 定义(.pl-empty)→ TestGUICSSClassesExist 红
改坏 helper 名 → TestGUIHelperFunctionsAreDefined 红
★ 第一次变异我改了 .pl-broken,判据**正确地没报**——因为它还被另一条规则定义。
这是变异选错目标,不是判据有洞;换 .pl-empty 后如期变红。
363 个测试全绿。
677 lines
13 KiB
CSS
677 lines
13 KiB
CSS
/* =====================================================================
|
|
ModelRouter Desktop GUI — Clash-Verge-style shell
|
|
Content area = full-screen iframe embedding the real WebUI 1:1.
|
|
Palette mirrors the WebUI (sakura/frost/glassmorphism).
|
|
===================================================================== */
|
|
:root {
|
|
--primary: #ff7fac;
|
|
--primary-h: #f33b7c;
|
|
--primary-50: #fff0f5;
|
|
--primary-100: #ffe4e9;
|
|
--primary-200: #ffcdd9;
|
|
--primary-300: #ff9eb5;
|
|
--primary-400: #ff7fac;
|
|
--primary-500: #f33b7c;
|
|
--secondary: #88c0d0;
|
|
--secondary-h: #4c8dae;
|
|
--danger: #db3694;
|
|
--ok: #17a964;
|
|
--warn: #b7791f;
|
|
--bg-s1: #eef2ff;
|
|
--bg-s2: #ffffff;
|
|
--bg-s3: #ffe9f0;
|
|
--card: rgba(255, 255, 255, 0.62);
|
|
--card-solid: #fff;
|
|
--line: rgba(255, 127, 172, 0.18);
|
|
--line-strong: rgba(120, 90, 150, 0.22);
|
|
--fg: #3b3350;
|
|
--muted: #7c7a95;
|
|
--blob1: rgba(255, 127, 172, 0.45);
|
|
--blob2: rgba(136, 192, 208, 0.42);
|
|
--blob3: rgba(244, 114, 182, 0.3);
|
|
--sh-sm: 0 1px 2px rgba(70, 50, 110, 0.06);
|
|
--sh-md: 0 6px 20px rgba(120, 90, 160, 0.13);
|
|
--sh-lg: 0 18px 46px rgba(120, 90, 160, 0.22);
|
|
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
}
|
|
html[data-theme="dark"] {
|
|
--primary: #f54180;
|
|
--primary-h: #f871a0;
|
|
--primary-50: #310413;
|
|
--primary-100: #610726;
|
|
--primary-200: #920b3a;
|
|
--primary-300: #c20e4d;
|
|
--primary-400: #f31260;
|
|
--primary-500: #f54180;
|
|
--secondary: #5e9fbf;
|
|
--secondary-h: #88c0d0;
|
|
--danger: #db3694;
|
|
--ok: #4f7cff;
|
|
--warn: #e0b25c;
|
|
--bg-s1: #14121c;
|
|
--bg-s2: #1a1824;
|
|
--bg-s3: #221a28;
|
|
--card: rgba(34, 31, 50, 0.55);
|
|
--card-solid: #241f38;
|
|
--line: rgba(255, 127, 172, 0.16);
|
|
--line-strong: rgba(200, 180, 235, 0.18);
|
|
--fg: #ece6f5;
|
|
--muted: #a09db8;
|
|
--blob1: rgba(255, 79, 160, 0.32);
|
|
--blob2: rgba(100, 180, 215, 0.26);
|
|
--blob3: rgba(219, 54, 148, 0.22);
|
|
--sh-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
|
|
--sh-md: 0 8px 26px rgba(0, 0, 0, 0.45);
|
|
--sh-lg: 0 20px 52px rgba(0, 0, 0, 0.55);
|
|
}
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
html,
|
|
body {
|
|
height: 100%;
|
|
}
|
|
body {
|
|
margin: 0;
|
|
color: var(--fg);
|
|
overflow: hidden;
|
|
font:
|
|
13px / 1.5 "Quicksand",
|
|
"Nunito",
|
|
"Inter",
|
|
-apple-system,
|
|
BlinkMacSystemFont,
|
|
"Segoe UI",
|
|
Roboto,
|
|
"PingFang SC",
|
|
"Microsoft YaHei",
|
|
sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
text-rendering: optimizeLegibility;
|
|
background: var(--bg-s1);
|
|
}
|
|
button {
|
|
font: inherit;
|
|
cursor: pointer;
|
|
border: none;
|
|
background: none;
|
|
color: inherit;
|
|
}
|
|
input {
|
|
font: inherit;
|
|
color: var(--fg);
|
|
}
|
|
::selection {
|
|
background: #ffcdba;
|
|
color: #fff;
|
|
}
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
::-webkit-scrollbar-thumb {
|
|
background: rgba(255, 182, 193, 0.45);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
/* animated gradient-mesh background */
|
|
#bgfx {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: -10;
|
|
overflow: hidden;
|
|
background: linear-gradient(135deg, var(--bg-s1), var(--bg-s2), var(--bg-s3));
|
|
}
|
|
#bgfx i {
|
|
position: absolute;
|
|
border-radius: 50%;
|
|
filter: blur(64px);
|
|
will-change: transform;
|
|
opacity: 0.6;
|
|
}
|
|
#bgfx .b1 {
|
|
top: -10%;
|
|
left: -8%;
|
|
width: 560px;
|
|
height: 560px;
|
|
background: var(--blob1);
|
|
animation: drift1 30s ease-in-out infinite alternate;
|
|
}
|
|
#bgfx .b2 {
|
|
top: 16%;
|
|
right: -10%;
|
|
width: 440px;
|
|
height: 440px;
|
|
background: var(--blob2);
|
|
animation: drift2 36s ease-in-out infinite alternate;
|
|
}
|
|
#bgfx .b3 {
|
|
bottom: -12%;
|
|
left: 24%;
|
|
width: 620px;
|
|
height: 620px;
|
|
background: var(--blob3);
|
|
animation: drift3 42s ease-in-out infinite alternate;
|
|
}
|
|
@keyframes drift1 {
|
|
from {
|
|
transform: translate(0, 0) scale(1);
|
|
}
|
|
to {
|
|
transform: translate(30px, -20px) scale(1.05);
|
|
}
|
|
}
|
|
@keyframes drift2 {
|
|
from {
|
|
transform: translate(0, 0) scale(1);
|
|
}
|
|
to {
|
|
transform: translate(-25px, 18px) scale(1.03);
|
|
}
|
|
}
|
|
@keyframes drift3 {
|
|
from {
|
|
transform: translate(0, 0) scale(1);
|
|
}
|
|
to {
|
|
transform: translate(18px, 25px) scale(1.04);
|
|
}
|
|
}
|
|
|
|
/* ---- frameless titlebar ---- */
|
|
.titlebar {
|
|
height: 36px;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 8px 0 14px;
|
|
-webkit-app-region: drag;
|
|
user-select: none;
|
|
background: linear-gradient(
|
|
180deg,
|
|
rgba(255, 255, 255, 0.5),
|
|
rgba(255, 255, 255, 0.16)
|
|
);
|
|
backdrop-filter: blur(14px) saturate(1.4);
|
|
-webkit-backdrop-filter: blur(14px) saturate(1.4);
|
|
border-bottom: 1px solid var(--line);
|
|
position: relative;
|
|
z-index: 30;
|
|
}
|
|
html[data-theme="dark"] .titlebar {
|
|
background: linear-gradient(
|
|
180deg,
|
|
rgba(26, 23, 38, 0.7),
|
|
rgba(26, 23, 38, 0.42)
|
|
);
|
|
}
|
|
.titlebar-brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 9px;
|
|
}
|
|
.tb-logo {
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 6px;
|
|
background: linear-gradient(
|
|
135deg,
|
|
#3f6ef5,
|
|
#6a8ffb
|
|
); /* matches site favicon */
|
|
color: #fff;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.tb-title {
|
|
font-size: 12.5px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.03em;
|
|
color: var(--fg);
|
|
}
|
|
.status-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: #c8c6d2;
|
|
}
|
|
.status-dot.ok {
|
|
background: var(--ok);
|
|
box-shadow: 0 0 8px rgba(23, 169, 100, 0.55);
|
|
}
|
|
.status-dot.bad {
|
|
background: var(--danger);
|
|
}
|
|
.titlebar-right {
|
|
margin-left: auto;
|
|
display: flex;
|
|
align-items: center;
|
|
height: 36px;
|
|
-webkit-app-region: no-drag;
|
|
gap: 2px;
|
|
}
|
|
.tb-btn {
|
|
width: 34px;
|
|
height: 30px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 7px;
|
|
color: var(--muted);
|
|
transition:
|
|
background 0.15s,
|
|
color 0.15s;
|
|
}
|
|
.tb-btn:hover {
|
|
background: var(--primary-100);
|
|
color: var(--fg);
|
|
}
|
|
.tb-btn.tb-close:hover {
|
|
background: var(--danger);
|
|
color: #fff;
|
|
}
|
|
|
|
/* ---- content area ---- */
|
|
#app {
|
|
position: absolute;
|
|
inset: 36px 0 0 0;
|
|
display: flex;
|
|
}
|
|
#rail {
|
|
flex: 0 0 56px;
|
|
width: 56px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 0;
|
|
background: var(--card2, rgba(255, 255, 255, 0.45));
|
|
border-right: 1px solid var(--line);
|
|
backdrop-filter: blur(12px);
|
|
-webkit-backdrop-filter: blur(12px);
|
|
}
|
|
.rail-gap {
|
|
flex: 1;
|
|
}
|
|
.rail-btn {
|
|
position: relative;
|
|
width: 38px;
|
|
height: 38px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 11px;
|
|
color: var(--muted);
|
|
transition: all 0.15s;
|
|
}
|
|
.rail-btn svg {
|
|
width: 18px;
|
|
height: 18px;
|
|
}
|
|
.rail-btn:hover {
|
|
color: var(--fg);
|
|
background: var(--primary-100);
|
|
}
|
|
.rail-btn.active {
|
|
color: var(--primary-h);
|
|
background: var(--primary-100);
|
|
}
|
|
/* toggle status dot (top-right corner of the button) */
|
|
.rail-dot {
|
|
position: absolute;
|
|
top: 5px;
|
|
right: 5px;
|
|
width: 7px;
|
|
height: 7px;
|
|
border-radius: 50%;
|
|
background: #c8c6d2;
|
|
border: 1.5px solid var(--card-solid, #fff);
|
|
box-sizing: content-box;
|
|
transition: background 0.15s;
|
|
}
|
|
.rail-dot.on {
|
|
background: var(--ok, #17a964);
|
|
box-shadow: 0 0 6px rgba(23, 169, 100, 0.6);
|
|
}
|
|
.theme-ic {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.theme-ic svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
/* content column: iframe sits to the right of the rail */
|
|
.app-main {
|
|
flex: 1;
|
|
min-width: 0;
|
|
position: relative;
|
|
}
|
|
.webui-frame {
|
|
width: 100%;
|
|
height: 100%;
|
|
border: none;
|
|
background: #fff;
|
|
}
|
|
html[data-theme="dark"] .webui-frame {
|
|
background: #1a1824;
|
|
}
|
|
|
|
/* ---- offline placeholder ---- */
|
|
.offline {
|
|
position: absolute;
|
|
inset: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 5;
|
|
}
|
|
.off-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 38px 46px;
|
|
background: var(--card);
|
|
border: 1px solid var(--line-strong);
|
|
border-radius: 20px;
|
|
box-shadow: var(--sh-lg);
|
|
backdrop-filter: blur(18px);
|
|
-webkit-backdrop-filter: blur(18px);
|
|
}
|
|
.off-logo {
|
|
width: 52px;
|
|
height: 52px;
|
|
border-radius: 15px;
|
|
background: linear-gradient(
|
|
135deg,
|
|
#3f6ef5,
|
|
#6a8ffb
|
|
); /* matches site favicon */
|
|
color: #fff;
|
|
font-size: 26px;
|
|
font-weight: 700;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: var(--sh-md);
|
|
}
|
|
.off-title {
|
|
font-size: 17px;
|
|
font-weight: 700;
|
|
}
|
|
.off-sub {
|
|
font-size: 12.5px;
|
|
color: var(--muted);
|
|
max-width: 340px;
|
|
text-align: center;
|
|
}
|
|
.off-card button {
|
|
padding: 10px 26px;
|
|
border-radius: 10px;
|
|
font-weight: 600;
|
|
margin-top: 6px;
|
|
}
|
|
.off-card .primary {
|
|
background: var(--primary);
|
|
color: #fff;
|
|
}
|
|
.off-card .primary:hover {
|
|
background: var(--primary-h);
|
|
}
|
|
|
|
/* loading spinner */
|
|
.console-loading {
|
|
position: absolute;
|
|
inset: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 14px;
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
z-index: 5;
|
|
}
|
|
.spinner {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 50%;
|
|
border: 3px solid var(--primary-100);
|
|
border-top-color: var(--primary-h);
|
|
animation: spin 0.8s linear infinite;
|
|
}
|
|
@keyframes spin {
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
/* ---- settings overlay ---- */
|
|
.overlay {
|
|
position: absolute;
|
|
inset: 36px 0 0 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 40;
|
|
background: rgba(30, 24, 48, 0.18);
|
|
backdrop-filter: blur(6px);
|
|
-webkit-backdrop-filter: blur(6px);
|
|
}
|
|
html[data-theme="dark"] .overlay {
|
|
background: rgba(10, 8, 18, 0.5);
|
|
}
|
|
.overlay-card {
|
|
width: 400px;
|
|
max-width: 92%;
|
|
background: var(--card-solid);
|
|
border: 1px solid var(--line-strong);
|
|
border-radius: 18px;
|
|
box-shadow: var(--sh-lg);
|
|
padding: 22px 24px;
|
|
animation: popIn 0.18s var(--ease-spring);
|
|
}
|
|
@keyframes popIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(8px) scale(0.98);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: none;
|
|
}
|
|
}
|
|
.overlay-head {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 10px;
|
|
}
|
|
.overlay-head h2 {
|
|
margin: 0;
|
|
font-size: 17px;
|
|
}
|
|
.form .row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 9px 0;
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
.form .row label {
|
|
flex: 0 0 110px;
|
|
font-size: 13px;
|
|
color: var(--muted);
|
|
}
|
|
.form .row .toggle {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
cursor: pointer;
|
|
font-size: 13px;
|
|
color: var(--fg);
|
|
font-weight: 500;
|
|
}
|
|
.form .row .toggle input {
|
|
accent-color: var(--primary);
|
|
width: 15px;
|
|
height: 15px;
|
|
}
|
|
.form .row .hint {
|
|
font-size: 11px;
|
|
color: var(--muted);
|
|
}
|
|
.form input[type="number"] {
|
|
width: 120px;
|
|
padding: 7px 10px;
|
|
border: 1px solid var(--line-strong);
|
|
border-radius: 8px;
|
|
background: var(--bg-s2);
|
|
outline: none;
|
|
}
|
|
.form input[type="number"]:focus {
|
|
border-color: var(--primary);
|
|
box-shadow: 0 0 0 3px var(--primary-100);
|
|
}
|
|
.form .actions {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
gap: 9px;
|
|
border-bottom: none;
|
|
padding-top: 12px;
|
|
}
|
|
.form .actions.right {
|
|
justify-content: flex-end;
|
|
}
|
|
.form .actions button {
|
|
padding: 9px 18px;
|
|
border-radius: 9px;
|
|
font-weight: 600;
|
|
transition: all 0.15s;
|
|
}
|
|
.form .actions .primary {
|
|
background: var(--primary);
|
|
color: #fff;
|
|
}
|
|
.form .actions .primary:hover {
|
|
background: var(--primary-h);
|
|
}
|
|
.form .actions .ghost {
|
|
background: var(--bg-s2);
|
|
border: 1px solid var(--line-strong);
|
|
color: var(--muted);
|
|
}
|
|
.form .actions .ghost:hover {
|
|
background: var(--primary-50);
|
|
color: var(--fg);
|
|
}
|
|
|
|
/* ---- toast ---- */
|
|
#toast {
|
|
position: fixed;
|
|
right: 20px;
|
|
bottom: 20px;
|
|
z-index: 200;
|
|
padding: 11px 18px;
|
|
border-radius: 12px;
|
|
background: var(--card-solid);
|
|
color: var(--fg);
|
|
font-size: 13px;
|
|
box-shadow: var(--sh-lg);
|
|
border: 1px solid var(--line);
|
|
opacity: 0;
|
|
transform: translateY(10px);
|
|
pointer-events: none;
|
|
transition: all 0.25s var(--ease-spring);
|
|
}
|
|
#toast.show {
|
|
opacity: 1;
|
|
transform: none;
|
|
}
|
|
#toast.err {
|
|
border-color: var(--danger);
|
|
}
|
|
|
|
/* ===== plugin management panel =========================================
|
|
* The existing .ghost/.primary rules are scoped to `.form .actions`, so a
|
|
* button outside that selector gets browser defaults. The plugin rows live in
|
|
* their own list, hence their own rules — reusing a scoped class here would have
|
|
* produced unstyled buttons that still worked, which is the kind of thing that
|
|
* looks fine until someone themes the shell.
|
|
*/
|
|
.pl-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
margin: 8px 0 4px;
|
|
}
|
|
.pl-item {
|
|
border: 1px solid var(--line);
|
|
border-radius: 9px;
|
|
padding: 10px 12px;
|
|
}
|
|
.pl-item.pl-broken {
|
|
border-color: var(--danger, #d1435b);
|
|
}
|
|
.pl-head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 13px;
|
|
}
|
|
.pl-builtin {
|
|
font-size: 10px;
|
|
padding: 1px 6px;
|
|
border-radius: 999px;
|
|
background: var(--primary-50);
|
|
color: var(--primary-h);
|
|
}
|
|
.pl-state {
|
|
margin-left: auto;
|
|
font-size: 11px;
|
|
color: var(--muted);
|
|
}
|
|
.pl-item.pl-broken .pl-state {
|
|
color: var(--danger, #d1435b);
|
|
}
|
|
.pl-desc {
|
|
font-size: 12px;
|
|
color: var(--muted);
|
|
margin-top: 3px;
|
|
}
|
|
.pl-err {
|
|
font-size: 11px;
|
|
color: var(--danger, #d1435b);
|
|
margin-top: 4px;
|
|
word-break: break-word;
|
|
}
|
|
.pl-acts {
|
|
margin-top: 8px;
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
.pl-acts button {
|
|
padding: 5px 12px;
|
|
font-size: 12px;
|
|
border-radius: 7px;
|
|
border: 1px solid var(--line);
|
|
background: var(--bg-s2, #fff);
|
|
color: var(--fg, inherit);
|
|
cursor: pointer;
|
|
transition: all 0.15s;
|
|
}
|
|
.pl-acts button:hover {
|
|
border-color: var(--primary);
|
|
color: var(--primary-h);
|
|
}
|
|
.pl-empty {
|
|
font-size: 12px;
|
|
color: var(--muted);
|
|
padding: 10px 0;
|
|
}
|