优化: 前端u-agent令牌对齐与组件拆分续批
- 全局样式类(df-sec-h/df-toast族)替换散装实现,主题token去硬编码兜底 - 拆分子组件:ai-chat/queue-panel(队列时钟与编辑随迁)、 conversation-sidebar/ConversationList、settings/provider-panel - 父组件死副本清理(队列编辑/时钟/键位表,TS6133清零)并补 fill-draft父侧转发(重构半成品断链修复) - 验收:vue-tsc EXIT 0
This commit is contained in:
@@ -52,6 +52,18 @@
|
||||
@keydown="onItemKeydown($event, conv.id, true)"
|
||||
>
|
||||
<div class="ai-conv-item-main">
|
||||
<!-- wave2 u-agent 范式: 左侧 7px 状态点。字段源 store.isGenerating(conv_state
|
||||
generating/stopping/compressed) / pendingCountOf(审批挂起) / loop_state='parked',
|
||||
无 error 字段故不映射;优先级 generating > 待审批 > parked > 灰 -->
|
||||
<span
|
||||
class="df-dot ai-conv-status"
|
||||
:class="store.isGenerating(conv.id) ? 'df-dot--run ai-conv-status--run'
|
||||
: pendingCountOf(conv.id) > 0 ? 'ai-conv-status--danger'
|
||||
: conv.loop_state === 'parked' ? 'ai-conv-status--paused'
|
||||
: 'df-dot--stop'"
|
||||
:title="store.isGenerating(conv.id) ? t('aiChat.generating') : undefined"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<!-- UX走查 P3-1: 搜索态此前无重命名入口(无 startRename 分支),与分组视图降级不一致;
|
||||
双击入口对齐分组视图,菜单入口供键盘用户(P2-6) -->
|
||||
<input
|
||||
@@ -70,7 +82,6 @@
|
||||
@dblclick.stop="startRename(conv)"
|
||||
>
|
||||
{{ conv.title || t('aiChat.newConversation') }}
|
||||
<span v-if="store.isGenerating(conv.id)" class="ai-conv-gen-dot" :title="t('aiChat.generating')"></span>
|
||||
<!-- V1: 待审批徽标(橙点计数,复用既有 aiChat.pendingApprovalCount 文案) -->
|
||||
<span v-if="pendingCountOf(conv.id) > 0" class="ai-conv-approve-badge" :title="t('aiChat.pendingApprovalCount', { n: pendingCountOf(conv.id) })">{{ pendingCountOf(conv.id) }}</span>
|
||||
<!-- 无人值守边界徽标:该会话已授权任务边界(镜像渐进填充);点击随会话切回显 -->
|
||||
@@ -145,6 +156,16 @@
|
||||
@keydown="onItemKeydown($event, conv.id, true)"
|
||||
>
|
||||
<div class="ai-conv-item-main">
|
||||
<!-- wave2 u-agent 范式: 左侧 7px 状态点(映射源同搜索态块注) -->
|
||||
<span
|
||||
class="df-dot ai-conv-status"
|
||||
:class="store.isGenerating(conv.id) ? 'df-dot--run ai-conv-status--run'
|
||||
: pendingCountOf(conv.id) > 0 ? 'ai-conv-status--danger'
|
||||
: conv.loop_state === 'parked' ? 'ai-conv-status--paused'
|
||||
: 'df-dot--stop'"
|
||||
:title="store.isGenerating(conv.id) ? t('aiChat.generating') : undefined"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<input
|
||||
v-if="editingConvId === conv.id"
|
||||
:ref="setEditEl"
|
||||
@@ -168,8 +189,6 @@
|
||||
class="ai-conv-item-title-badge"
|
||||
width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"
|
||||
><polyline points="20 6 9 17 4 12"/></svg>
|
||||
<!-- F-09: 多会话并发生成指示器(后台/当前会话生成中均显脉冲点,多会话可同时显示) -->
|
||||
<span v-if="store.isGenerating(conv.id)" class="ai-conv-gen-dot" :title="t('aiChat.generating')"></span>
|
||||
<!-- V1: 待审批徽标(橙点计数,复用既有 aiChat.pendingApprovalCount 文案) -->
|
||||
<span v-if="pendingCountOf(conv.id) > 0" class="ai-conv-approve-badge" :title="t('aiChat.pendingApprovalCount', { n: pendingCountOf(conv.id) })">{{ pendingCountOf(conv.id) }}</span>
|
||||
<!-- 无人值守边界徽标:该会话已授权任务边界(镜像渐进填充);点击随会话切回显 -->
|
||||
@@ -225,6 +244,8 @@
|
||||
@keydown.space.prevent="onRecover(conv.id)"
|
||||
>
|
||||
<div class="ai-conv-item-main">
|
||||
<!-- wave2: 本组数据源 ai_list_recoverable(loop_state='parked' 完备真相),整组恒 parked→静态琥珀点 -->
|
||||
<span class="df-dot ai-conv-status ai-conv-status--paused" aria-hidden="true"></span>
|
||||
<span class="ai-conv-item-title">
|
||||
{{ conv.title || t('aiChat.newConversation') }}
|
||||
<span class="ai-conv-recover-badge">⏸</span>
|
||||
@@ -260,9 +281,18 @@
|
||||
@keydown="onItemKeydown($event, conv.id)"
|
||||
>
|
||||
<div class="ai-conv-item-main">
|
||||
<!-- wave2 u-agent 范式: 归档项同款状态点(归档为静息态,默认落灰) -->
|
||||
<span
|
||||
class="df-dot ai-conv-status"
|
||||
:class="store.isGenerating(conv.id) ? 'df-dot--run ai-conv-status--run'
|
||||
: pendingCountOf(conv.id) > 0 ? 'ai-conv-status--danger'
|
||||
: conv.loop_state === 'parked' ? 'ai-conv-status--paused'
|
||||
: 'df-dot--stop'"
|
||||
:title="store.isGenerating(conv.id) ? t('aiChat.generating') : undefined"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span class="ai-conv-item-title" :title="conv.title || ''">
|
||||
{{ conv.title || t('aiChat.newConversation') }}
|
||||
<span v-if="store.isGenerating(conv.id)" class="ai-conv-gen-dot" :title="t('aiChat.generating')"></span>
|
||||
<!-- V1: 待审批徽标(橙点计数,复用既有 aiChat.pendingApprovalCount 文案) -->
|
||||
<span v-if="pendingCountOf(conv.id) > 0" class="ai-conv-approve-badge" :title="t('aiChat.pendingApprovalCount', { n: pendingCountOf(conv.id) })">{{ pendingCountOf(conv.id) }}</span>
|
||||
<!-- 无人值守边界徽标:该会话已授权任务边界(镜像渐进填充);点击随会话切回显 -->
|
||||
@@ -296,6 +326,17 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- F4-1: 会话列表分页——底部「加载更多」续拉下一页(首屏 50/组,归档组同口径放开) -->
|
||||
<div
|
||||
v-if="convListPaging.hasMore"
|
||||
class="ai-conv-load-more"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="onLoadMore"
|
||||
@keydown.enter.prevent="onLoadMore"
|
||||
@keydown.space.prevent="onLoadMore"
|
||||
>{{ convListPaging.loading ? t('common.loading') : t('gitChanges.loadMore') }}</div>
|
||||
|
||||
<div v-if="store.state.conversations.length === 0" class="ai-conv-empty">
|
||||
{{ t('aiChat.emptyConversation') }}
|
||||
</div>
|
||||
@@ -338,7 +379,8 @@ import { convPendingApprovals } from '../../composables/ai/aiShared'
|
||||
// 镜像由边界配置卡在切会话/写入时渐进填充,无批量 IPC 属契约限制)
|
||||
import { getConvBoundary, fetchConvBoundary } from '../../composables/ai/useTaskBoundary'
|
||||
// UX走查 P2-7 只读订阅:rename/archive/pin 失败通知由 useAiConversations 发布,本组件弹就近 toast
|
||||
import { convOpFailNotice } from '../../composables/ai/useAiConversations'
|
||||
// F4-1: 会话列表分页游标(reactive,绑「加载更多」显隐/loading)+ 续拉/拉满动作
|
||||
import { convOpFailNotice, convListPaging, loadMoreConversations, loadAllConversations } from '../../composables/ai/useAiConversations'
|
||||
import { useToast } from '../../composables/useToast'
|
||||
import { formatRelative } from '../../utils/time'
|
||||
import type { AiConversationSummary } from '../../api/types'
|
||||
@@ -662,6 +704,28 @@ const searchResults = computed<AiConversationSummary[] | null>(
|
||||
() => store.filteredConversations.value,
|
||||
)
|
||||
|
||||
// ── F4-1: 会话列表分页(加载更多按钮 + 搜索全量兜底) ──
|
||||
|
||||
/** 「加载更多」:续拉下一页(失败/无新增静默,按钮 loading 态由 convListPaging 驱动) */
|
||||
function onLoadMore(): void {
|
||||
void loadMoreConversations()
|
||||
}
|
||||
|
||||
// 搜索输入即拉满:搜索过滤(stores.filteredConversations)跑在已加载列表上,未拉满时
|
||||
// 第 51 条起的会话搜不到(硬 50 截断=搜索失灵)——输入非空且还有未拉页时一次拉尽,
|
||||
// 之后搜索自然覆盖全量;已拉满(hasMore=false)零开销不触发。
|
||||
watch(() => store.state.searchQuery, (q) => {
|
||||
if (q.trim() && convListPaging.hasMore && !convListPaging.loading) {
|
||||
void loadAllConversations()
|
||||
}
|
||||
})
|
||||
|
||||
// 加载更多/搜索拉满追加的新会话补拉边界徽标镜像(A10 空洞随列表加深扩大;
|
||||
// hydrate 对已入镜像的会话跳过,常规刷新时零 IPC)
|
||||
watch(() => store.state.conversations.length, () => {
|
||||
void hydrateBoundaryBadges()
|
||||
})
|
||||
|
||||
// ── 侧栏宽度拖拽 ──
|
||||
const resizeState = { startX: 0, startWidth: 0 }
|
||||
function onResizeMove(e: MouseEvent) {
|
||||
@@ -756,7 +820,7 @@ defineExpose({
|
||||
transition: background 0.15s var(--df-ease);
|
||||
}
|
||||
.ai-sidebar-resize-handle:hover {
|
||||
background: var(--df-accent, #4f8cff);
|
||||
background: var(--df-accent);
|
||||
opacity: 0.5;
|
||||
}
|
||||
/* 拖拽中全局锁 cursor + 禁选中文本(挂在 body 上,跨元素生效) */
|
||||
@@ -778,13 +842,21 @@ body.ai-sidebar-resizing * {
|
||||
color: var(--df-text);
|
||||
}
|
||||
|
||||
/* UX-06 §3.1: 对话搜索框(紧贴 header 下,样式跟随周围输入控件) */
|
||||
/* UX-06 §3.1: 对话搜索框(wave2 u-agent .sb-search 范式:容器成框,卡底+focus 边框 info) */
|
||||
.ai-conv-search {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 10px;
|
||||
border-bottom: 0.5px solid var(--df-border);
|
||||
margin: 8px 10px;
|
||||
padding: 0 8px;
|
||||
height: 30px;
|
||||
background: var(--df-bg-card);
|
||||
border: 0.5px solid var(--df-border);
|
||||
border-radius: var(--df-radius-sm);
|
||||
transition: border-color 0.15s var(--df-ease);
|
||||
}
|
||||
.ai-conv-search:focus-within {
|
||||
border-color: var(--df-info);
|
||||
}
|
||||
.ai-conv-search-icon {
|
||||
flex: none;
|
||||
@@ -800,8 +872,8 @@ body.ai-sidebar-resizing * {
|
||||
font-size: 12px;
|
||||
color: var(--df-text);
|
||||
}
|
||||
/* 聚焦可见环:无边框输入聚焦无变化,且 scoped 选择器特异性高于全局 :focus-visible 兜底,须就地补环,仅键盘聚焦显示 */
|
||||
.ai-conv-search-input:focus-visible { outline: 2px solid var(--df-accent); outline-offset: 1px; }
|
||||
/* 聚焦可见环:容器已随 :focus-within 变 info 边框,键盘环同色补强(scoped 特异性须盖全局兜底) */
|
||||
.ai-conv-search-input:focus-visible { outline: 2px solid var(--df-info); outline-offset: 1px; }
|
||||
.ai-conv-search-input::placeholder {
|
||||
color: var(--df-text-dim);
|
||||
}
|
||||
@@ -819,12 +891,15 @@ body.ai-sidebar-resizing * {
|
||||
.ai-conv-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 4px;
|
||||
padding: 4px 6px; /* u-agent .sb-list 范式;滚动条走 global ::-webkit-scrollbar token 体系 */
|
||||
}
|
||||
/* wave2 u-agent 范式: 分组头 10px uppercase + letter-spacing 1px + dim */
|
||||
.ai-conv-group-title {
|
||||
padding: 8px 8px 3px;
|
||||
font-size: 11px;
|
||||
padding: 10px 8px 4px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: var(--df-text-dim);
|
||||
user-select: none;
|
||||
}
|
||||
@@ -851,7 +926,10 @@ body.ai-sidebar-resizing * {
|
||||
.ai-conv-group-count {
|
||||
margin-left: 2px;
|
||||
font-weight: 400;
|
||||
opacity: 0.7;
|
||||
font-family: var(--df-font-mono); /* mono 计数(df-mono 范式),upper/lsp 回退保证数字不散 */
|
||||
font-size: 10px;
|
||||
letter-spacing: 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.ai-conv-group-empty {
|
||||
padding: 2px 8px 4px 20px;
|
||||
@@ -859,40 +937,47 @@ body.ai-sidebar-resizing * {
|
||||
color: var(--df-text-dim);
|
||||
font-style: italic;
|
||||
}
|
||||
/* wave2 u-agent .cv 范式: 单行条目,8px 级密度;hover/active 同走灰阶卡底(GitHub 两级同底、
|
||||
active 由左 2px accent 实线区分),深浅主题同 token 自动适配 */
|
||||
.ai-conv-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 6px 8px;
|
||||
padding: 8px;
|
||||
border-radius: var(--df-radius);
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
gap: 4px;
|
||||
gap: 6px;
|
||||
}
|
||||
.ai-conv-item:hover {
|
||||
background: var(--df-sidebar-hover);
|
||||
background: var(--df-bg-card-hover);
|
||||
}
|
||||
.ai-conv-item--active {
|
||||
background: var(--df-sidebar-active);
|
||||
background: var(--df-bg-card-hover);
|
||||
box-shadow: inset 2px 0 0 var(--df-accent);
|
||||
}
|
||||
/* 切换进行时(UX走查 P1):与活跃项同底色示"已选中"(乐观预高亮),降不透明度示"加载中";
|
||||
IPC 返回后 activeConversationId 落定、本类移除,视觉无缝衔接;失败回滚高亮回原活跃项 */
|
||||
.ai-conv-item--switching {
|
||||
background: var(--df-sidebar-active);
|
||||
background: var(--df-bg-card-hover);
|
||||
opacity: 0.7;
|
||||
}
|
||||
.ai-conv-item--archived .ai-conv-item-title {
|
||||
opacity: 0.6;
|
||||
}
|
||||
/* wave2: 行内布局对齐 .cv-top([状态点][标题省略][时间]) */
|
||||
.ai-conv-item-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.ai-conv-item-title {
|
||||
font-size: 13px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--df-text);
|
||||
white-space: nowrap;
|
||||
@@ -902,7 +987,6 @@ body.ai-sidebar-resizing * {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
min-width: 0;
|
||||
}
|
||||
/* UX-2025-20 §7.4: AI 生成标题淡入(0.3s opacity + transform 过渡) */
|
||||
.ai-conv-item-title--flash {
|
||||
@@ -917,34 +1001,35 @@ body.ai-sidebar-resizing * {
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
.ai-conv-item-edit {
|
||||
flex: 1 1 0; /* wave2: main 已行化,输入框按 flex 填充(min-width:0 防 input 固有宽撑破窄侧栏) */
|
||||
min-width: 0;
|
||||
width: auto;
|
||||
font-size: 11px;
|
||||
color: var(--df-text);
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
padding: 0 4px;
|
||||
border: 1px solid var(--df-accent);
|
||||
border-radius: var(--df-radius-sm);
|
||||
background: var(--df-bg, #fff);
|
||||
background: var(--df-bg);
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 聚焦可见环:边框恒为 accent 聚焦无变化,且 scoped 选择器特异性高于全局 :focus-visible 兜底,须就地补环,仅键盘聚焦显示 */
|
||||
.ai-conv-item-edit:focus-visible { outline: 2px solid var(--df-accent); outline-offset: 1px; }
|
||||
/* wave2 u-agent 范式: 时间/计数元数据 mono 10px dim(.df-mono 同款视觉) */
|
||||
.ai-conv-item-time {
|
||||
font-size: 9px;
|
||||
flex: none;
|
||||
font-family: var(--df-font-mono);
|
||||
font-size: 10px;
|
||||
color: var(--df-text-dim);
|
||||
}
|
||||
/* 多会话并发生成指示器(会话项标题行内脉冲点,多会话可同时显示) */
|
||||
.ai-conv-gen-dot {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--df-accent);
|
||||
flex-shrink: 0;
|
||||
animation: ai-conv-gen-pulse 1.4s ease-in-out infinite;
|
||||
}
|
||||
/* wave2 u-agent .st 状态点变体: 公共 .df-dot/.df-dot--run/.df-dot--stop 管造型与绿/灰底,
|
||||
此处只补动效与缺位色(全 token): run 绿+glow+脉冲(global df-pulse)、danger 待审批红、paused 停靠琥珀 */
|
||||
.ai-conv-status--run { box-shadow: 0 0 6px var(--df-success); animation: df-pulse 1.5s infinite; }
|
||||
.ai-conv-status--danger { background: var(--df-danger); }
|
||||
.ai-conv-status--paused { background: var(--df-warning); }
|
||||
@keyframes ai-conv-gen-pulse {
|
||||
/* 供 .ai-conv-recover-dot 分组级脉冲用(原行内 gen-dot 已由状态点接管) */
|
||||
0%, 100% { opacity: 0.35; transform: scale(0.85); }
|
||||
50% { opacity: 1; transform: scale(1.15); }
|
||||
}
|
||||
@@ -955,7 +1040,7 @@ body.ai-sidebar-resizing * {
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
background: var(--df-warning, #f0c75e);
|
||||
background: var(--df-warning);
|
||||
animation: ai-conv-gen-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
.ai-conv-item--recoverable {
|
||||
@@ -966,7 +1051,7 @@ body.ai-sidebar-resizing * {
|
||||
margin-left: 2px;
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
color: var(--df-warning, #f0c75e);
|
||||
color: var(--df-warning);
|
||||
cursor: help;
|
||||
}
|
||||
/* V1: 跨会话待审批徽标(橙点计数,warning 色系与待审批语义一致;固定尺寸不挤标题换行) */
|
||||
@@ -975,7 +1060,7 @@ body.ai-sidebar-resizing * {
|
||||
height: 12px;
|
||||
padding: 0 3px;
|
||||
border-radius: 6px;
|
||||
background: var(--df-warning, #f0c75e);
|
||||
background: var(--df-warning);
|
||||
color: #000;
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
@@ -1032,9 +1117,13 @@ body.ai-sidebar-resizing * {
|
||||
color: var(--df-text);
|
||||
}
|
||||
/* 已置顶对话视觉强调(左边线,不依赖菜单展开,一眼识别)。
|
||||
pinned 类挂在 .ai-conv-item 上(操作收进更多菜单后,按钮不再常驻,改 item 级类)。 */
|
||||
pinned 类挂在 .ai-conv-item 上(操作收进更多菜单后,按钮不再常驻,改 item 级类)。
|
||||
wave2: 选中态同占左缘,置顶线降为 45% 透明 accent 让位,选中实线优先 */
|
||||
.ai-conv-item--pinned {
|
||||
box-shadow: inset 2px 0 0 var(--df-accent, var(--df-text));
|
||||
box-shadow: inset 2px 0 0 color-mix(in srgb, var(--df-accent) 45%, transparent);
|
||||
}
|
||||
.ai-conv-item--active.ai-conv-item--pinned {
|
||||
box-shadow: inset 2px 0 0 var(--df-accent);
|
||||
}
|
||||
/* ── 「更多」操作菜单(置顶/归档/删除)──
|
||||
「...」按钮 hover 时随 actions 浮出(同原 4 按钮交互),点击展开下拉。
|
||||
@@ -1056,8 +1145,8 @@ body.ai-sidebar-resizing * {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
background: var(--df-bg, #fff);
|
||||
border: 1px solid var(--df-border, #e2e2e2);
|
||||
background: var(--df-bg);
|
||||
border: 1px solid var(--df-border);
|
||||
border-radius: var(--df-radius-sm);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
@@ -1088,6 +1177,21 @@ body.ai-sidebar-resizing * {
|
||||
font-size: 11px;
|
||||
color: var(--df-text-dim);
|
||||
}
|
||||
/* F4-1: 列表底部「加载更多」(轻量文字行,hover 同列表项灰阶卡底范式) */
|
||||
.ai-conv-load-more {
|
||||
margin: 4px 2px;
|
||||
padding: 6px 8px;
|
||||
border-radius: var(--df-radius);
|
||||
font-size: 11px;
|
||||
color: var(--df-text-dim);
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.ai-conv-load-more:hover {
|
||||
background: var(--df-bg-card-hover);
|
||||
color: var(--df-text);
|
||||
}
|
||||
|
||||
/* UX走查 P2-7: 会话操作失败就近 toast(仅 error 一型)。视觉对齐 AiChat .ai-toast,
|
||||
但 AiChat 版是 scoped 样式渗透不进来,故自持一份;侧栏窄,max-width 收 85% */
|
||||
@@ -1102,7 +1206,7 @@ body.ai-sidebar-resizing * {
|
||||
z-index: 30; /* 高于操作菜单(20),菜单展开时失败反馈不被盖 */
|
||||
max-width: 85%;
|
||||
overflow-wrap: anywhere;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: var(--df-shadow-sm);
|
||||
background: var(--df-danger-bg);
|
||||
color: var(--df-danger);
|
||||
border: 0.5px solid var(--df-danger);
|
||||
|
||||
Reference in New Issue
Block a user