- 自动化测试核心(packages/core/src/testing.ts):
- JSONPath 求值($.a.b[0].c)、断言评估(状态码/耗时/Header/Body × 10 种操作)
- 变量提取(响应 → 当前环境,{{var}} 链式传值)、Markdown 报告导出
- 用例模型:场景用例 = 有序步骤(引用 HTTP 接口 + 断言 + 提取),失败后继续可选
- 执行器 testRunner + 定时调度器 scheduler(应用运行期间按间隔自动执行)
- TestingView:用例列表 / 步骤编排 / 断言与提取编辑器 / 报告(统计卡+断言明细+过滤+再次运行+导出)
- AI 助手:OpenAI 兼容客户端(DeepSeek/智谱/Kimi/OpenAI/Ollama 预设)
- 设置 → AI 助手:供应商 CRUD + 测试连接 + 当前使用切换
- AI 生成场景用例:读取集合接口清单 → 编排步骤/断言/提取 → 预览保存
- 入口:侧栏底部「自动化测试」+ ⋯ 菜单;文档新增 /docs/testing /docs/ai
- 新增 11 个引擎单测(总 48 前端用例全绿);本地 E2E:运行通过+断言通过+petId 提取验证
568 lines
19 KiB
CSS
568 lines
19 KiB
CSS
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
/* ============ 主题变量 ============ */
|
|
:root,
|
|
:root[data-theme="dark"] {
|
|
color-scheme: dark;
|
|
--bg: #0b1220;
|
|
--panel: #0f1726;
|
|
--panel-2: #131e33;
|
|
--panel-3: #182540;
|
|
--border: #1e2b45;
|
|
--border-strong: #2b3b5c;
|
|
--text: #dbe4f3;
|
|
--text-dim: #8b99b0;
|
|
--text-faint: #5c6a83;
|
|
--accent: #3b82f6;
|
|
--accent-strong: #60a5fa;
|
|
--accent-soft: rgba(59, 130, 246, 0.14);
|
|
--success: #10b981;
|
|
--warn: #f59e0b;
|
|
--danger: #f43f5e;
|
|
--input-bg: #0a1120;
|
|
--code-bg: #070c16;
|
|
--hover: rgba(148, 163, 184, 0.08);
|
|
--active: rgba(59, 130, 246, 0.12);
|
|
--scrollbar: #334155;
|
|
|
|
--m-GET: #34d399;
|
|
--m-POST: #fbbf24;
|
|
--m-PUT: #60a5fa;
|
|
--m-PATCH: #c084fc;
|
|
--m-DELETE: #fb7185;
|
|
--m-HEAD: #94a3b8;
|
|
--m-OPTIONS: #38bdf8;
|
|
}
|
|
|
|
:root[data-theme="light"] {
|
|
color-scheme: light;
|
|
--bg: #eef1f6;
|
|
--panel: #ffffff;
|
|
--panel-2: #f6f8fb;
|
|
--panel-3: #eef2f8;
|
|
--border: #e3e8f0;
|
|
--border-strong: #cbd5e1;
|
|
--text: #1c2434;
|
|
--text-dim: #5b6880;
|
|
--text-faint: #8a95a9;
|
|
--accent: #2563eb;
|
|
--accent-strong: #1d4ed8;
|
|
--accent-soft: rgba(37, 99, 235, 0.1);
|
|
--success: #059669;
|
|
--warn: #d97706;
|
|
--danger: #e11d48;
|
|
--input-bg: #ffffff;
|
|
--code-bg: #f5f7fa;
|
|
--hover: rgba(100, 116, 139, 0.08);
|
|
--active: rgba(37, 99, 235, 0.1);
|
|
--scrollbar: #c4cdda;
|
|
|
|
--m-GET: #059669;
|
|
--m-POST: #d97706;
|
|
--m-PUT: #2563eb;
|
|
--m-PATCH: #9333ea;
|
|
--m-DELETE: #e11d48;
|
|
--m-HEAD: #64748b;
|
|
--m-OPTIONS: #0284c7;
|
|
}
|
|
|
|
@layer base {
|
|
* {
|
|
scrollbar-color: var(--scrollbar) transparent;
|
|
scrollbar-width: thin;
|
|
}
|
|
body {
|
|
@apply m-0 font-sans antialiased;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
}
|
|
button,
|
|
input,
|
|
select,
|
|
textarea {
|
|
font: inherit;
|
|
color: inherit;
|
|
}
|
|
::selection {
|
|
background: var(--accent-soft);
|
|
}
|
|
}
|
|
|
|
@layer components {
|
|
/* ---------- 文本工具 ---------- */
|
|
.ops-dim { color: var(--text-dim); }
|
|
.ops-faint { color: var(--text-faint); }
|
|
.ops-accent { color: var(--accent-strong); }
|
|
.ops-success { color: var(--success); }
|
|
.ops-warn { color: var(--warn); }
|
|
.ops-danger { color: var(--danger); }
|
|
|
|
/* ---------- 面板 ---------- */
|
|
.ops-card {
|
|
background: var(--panel);
|
|
border: 1px solid var(--border);
|
|
border-radius: 0.75rem;
|
|
}
|
|
.ops-panel { background: var(--panel); }
|
|
.ops-panel-2 { background: var(--panel-2); }
|
|
.ops-border { border-color: var(--border); }
|
|
.ops-divider { border-bottom: 1px solid var(--border); }
|
|
|
|
/* ---------- 输入 ---------- */
|
|
.ops-input {
|
|
width: 100%;
|
|
background: var(--input-bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 0.5rem;
|
|
padding: 0.4rem 0.7rem;
|
|
font-size: 0.8125rem;
|
|
color: var(--text);
|
|
outline: none;
|
|
transition: border-color 0.15s, box-shadow 0.15s;
|
|
}
|
|
.ops-input::placeholder { color: var(--text-faint); }
|
|
.ops-input:focus {
|
|
border-color: var(--accent);
|
|
box-shadow: 0 0 0 3px var(--accent-soft);
|
|
}
|
|
.ops-input-sm {
|
|
padding: 0.25rem 0.5rem;
|
|
font-size: 0.75rem;
|
|
}
|
|
.ops-label {
|
|
display: block;
|
|
margin-bottom: 0.375rem;
|
|
font-size: 0.6875rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
color: var(--text-faint);
|
|
}
|
|
|
|
/* ---------- 按钮 ---------- */
|
|
.ops-btn {
|
|
@apply inline-flex items-center justify-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold transition;
|
|
border: 1px solid transparent;
|
|
cursor: pointer;
|
|
}
|
|
.ops-btn:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
.ops-btn-primary { background: var(--accent); color: #fff; }
|
|
.ops-btn-primary:hover:not(:disabled) { background: var(--accent-strong); }
|
|
.ops-btn-success { background: var(--success); color: #fff; }
|
|
.ops-btn-success:hover:not(:disabled) { filter: brightness(1.1); }
|
|
.ops-btn-danger { background: var(--danger); color: #fff; }
|
|
.ops-btn-danger:hover:not(:disabled) { filter: brightness(1.1); }
|
|
.ops-btn-secondary {
|
|
background: var(--panel-2);
|
|
border-color: var(--border);
|
|
color: var(--text);
|
|
}
|
|
.ops-btn-secondary:hover:not(:disabled) { border-color: var(--border-strong); background: var(--hover); }
|
|
.ops-btn-ghost {
|
|
background: transparent;
|
|
color: var(--text-dim);
|
|
}
|
|
.ops-btn-ghost:hover:not(:disabled) { background: var(--hover); color: var(--text); }
|
|
|
|
/* ---------- chip / badge ---------- */
|
|
.ops-chip {
|
|
@apply rounded-full px-2.5 py-1 text-[11px] font-medium transition;
|
|
border: 1px solid var(--border);
|
|
background: var(--panel-2);
|
|
color: var(--text-dim);
|
|
}
|
|
.ops-chip:hover { border-color: var(--accent); color: var(--accent-strong); }
|
|
.ops-chip-active {
|
|
background: var(--accent-soft);
|
|
border-color: var(--accent);
|
|
color: var(--accent-strong);
|
|
}
|
|
|
|
/* ---------- Tab ---------- */
|
|
.ops-tab {
|
|
@apply px-3.5 py-2 text-xs font-medium transition;
|
|
color: var(--text-faint);
|
|
border-bottom: 2px solid transparent;
|
|
white-space: nowrap;
|
|
}
|
|
.ops-tab:hover { color: var(--text); }
|
|
.ops-tab-active {
|
|
color: var(--accent-strong);
|
|
border-bottom-color: var(--accent);
|
|
}
|
|
.ops-tab .ops-count {
|
|
@apply ml-1 rounded-full px-1.5 text-[10px];
|
|
background: var(--hover);
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
/* ---------- 代码 ---------- */
|
|
.ops-code {
|
|
background: var(--code-bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 0.5rem;
|
|
padding: 0.75rem;
|
|
font-family: "JetBrains Mono", SFMono-Regular, Menlo, monospace;
|
|
font-size: 0.75rem;
|
|
line-height: 1.55;
|
|
color: var(--text);
|
|
outline: none;
|
|
}
|
|
.ops-code:focus { border-color: var(--accent); }
|
|
.ops-code-no-bg {
|
|
background: transparent;
|
|
border: none;
|
|
padding: 0;
|
|
}
|
|
|
|
/* ---------- 方法色 ---------- */
|
|
.ops-method-GET { color: var(--m-GET); }
|
|
.ops-method-POST { color: var(--m-POST); }
|
|
.ops-method-PUT { color: var(--m-PUT); }
|
|
.ops-method-PATCH { color: var(--m-PATCH); }
|
|
.ops-method-DELETE { color: var(--m-DELETE); }
|
|
.ops-method-HEAD { color: var(--m-HEAD); }
|
|
.ops-method-OPTIONS { color: var(--m-OPTIONS); }
|
|
.ops-method-badge {
|
|
@apply inline-block rounded px-1.5 py-0.5 text-[10px] font-bold tracking-wide;
|
|
background: var(--hover);
|
|
}
|
|
|
|
/* ---------- 树 ---------- */
|
|
.ops-tree-row {
|
|
@apply flex items-center gap-1.5 rounded-md px-2 py-1.5 text-[13px] cursor-pointer transition;
|
|
color: var(--text-dim);
|
|
}
|
|
.ops-tree-row:hover { background: var(--hover); color: var(--text); }
|
|
.ops-tree-row-active {
|
|
background: var(--active);
|
|
color: var(--accent-strong);
|
|
}
|
|
|
|
/* ---------- 弹窗 ---------- */
|
|
.ops-modal-mask {
|
|
@apply fixed inset-0 z-50 grid place-items-center p-6;
|
|
background: rgba(2, 6, 18, 0.55);
|
|
backdrop-filter: blur(2px);
|
|
}
|
|
.ops-modal {
|
|
@apply w-full max-w-lg rounded-2xl shadow-2xl flex flex-col overflow-hidden;
|
|
background: var(--panel);
|
|
border: 1px solid var(--border);
|
|
max-height: 85vh;
|
|
}
|
|
.ops-modal-header {
|
|
@apply flex items-center justify-between px-5 py-3.5;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.ops-modal-title { @apply text-sm font-semibold; }
|
|
.ops-modal-body { @apply px-5 py-4 overflow-auto; }
|
|
.ops-modal-footer {
|
|
@apply flex items-center justify-end gap-2 px-5 py-3;
|
|
border-top: 1px solid var(--border);
|
|
background: var(--panel-2);
|
|
}
|
|
|
|
/* ---------- 右键菜单 ---------- */
|
|
.ops-menu {
|
|
@apply fixed z-[60] min-w-[180px] rounded-xl py-1.5 shadow-2xl;
|
|
background: var(--panel);
|
|
border: 1px solid var(--border);
|
|
}
|
|
.ops-menu-item {
|
|
@apply flex w-full items-center gap-2.5 px-3.5 py-1.5 text-left text-[13px] transition;
|
|
color: var(--text-dim);
|
|
}
|
|
.ops-menu-item:hover { background: var(--hover); color: var(--text); }
|
|
.ops-menu-item-danger:hover { color: var(--danger); }
|
|
.ops-menu-sep { border-top: 1px solid var(--border); margin: 0.375rem 0; }
|
|
|
|
/* ---------- 实时消息 ---------- */
|
|
.ops-msg {
|
|
@apply rounded-lg border px-2.5 py-1.5 font-mono text-[12px] leading-5 whitespace-pre-wrap break-words;
|
|
}
|
|
.ops-msg-in { border-color: color-mix(in srgb, var(--success) 30%, transparent); background: color-mix(in srgb, var(--success) 8%, transparent); }
|
|
.ops-msg-out { border-color: color-mix(in srgb, var(--accent) 30%, transparent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
|
|
.ops-msg-sys { background: var(--hover); border-color: var(--border); color: var(--text-dim); }
|
|
.ops-msg-err { border-color: color-mix(in srgb, var(--danger) 40%, transparent); background: color-mix(in srgb, var(--danger) 8%, transparent); }
|
|
|
|
/* ---------- JSON 高亮 ---------- */
|
|
.ops-json-key { color: var(--accent-strong); }
|
|
.ops-json-str { color: var(--success); }
|
|
.ops-json-num { color: var(--m-POST); }
|
|
.ops-json-bool { color: var(--m-PATCH); }
|
|
.ops-json-null { color: var(--text-faint); }
|
|
|
|
/* ---------- 状态点 ---------- */
|
|
.ops-dot { @apply inline-block h-2 w-2 rounded-full; }
|
|
.ops-dot-open { background: var(--success); box-shadow: 0 0 6px var(--success); }
|
|
.ops-dot-connecting { background: var(--warn); animation: ops-pulse 1s infinite; }
|
|
.ops-dot-closed { background: var(--text-faint); }
|
|
.ops-dot-error { background: var(--danger); }
|
|
|
|
/* ---------- 滚动区 ---------- */
|
|
.ops-scroll { overflow: auto; }
|
|
|
|
/* ---------- 环境徽标 ---------- */
|
|
.env-badge {
|
|
display: inline-grid;
|
|
place-items: center;
|
|
border-radius: 6px;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
border: 1px solid transparent;
|
|
flex-shrink: 0;
|
|
user-select: none;
|
|
}
|
|
|
|
/* ---------- 顶栏环境切换器 ---------- */
|
|
.ops-envswitch {
|
|
@apply inline-flex items-center gap-2 rounded-lg px-2.5 py-1.5 text-xs font-medium transition;
|
|
border: 1px solid var(--border);
|
|
background: var(--panel-2);
|
|
color: var(--text);
|
|
max-width: 240px;
|
|
}
|
|
.ops-envswitch:hover { border-color: var(--border-strong); }
|
|
|
|
/* ---------- 菜单项 flex(支持徽标行) ---------- */
|
|
.ops-menu-item-flex { display: flex; align-items: center; width: 100%; }
|
|
|
|
/* ---------- 环境管理弹窗 ---------- */
|
|
.env-dialog { display: grid; grid-template-columns: 196px minmax(0, 1fr); gap: 0 18px; min-height: 420px; margin: 0 -1.25rem -0.25rem; }
|
|
.env-dialog-side {
|
|
border-right: 1px solid var(--border);
|
|
padding-right: 14px;
|
|
display: flex; flex-direction: column; gap: 2px;
|
|
max-height: 60vh; overflow-y: auto;
|
|
}
|
|
.env-dialog-group { @apply mb-1.5 px-2 text-[11px] font-semibold uppercase tracking-[0.08em]; color: var(--text-faint); }
|
|
.env-dialog-item {
|
|
@apply flex items-center gap-2 rounded-lg px-2 py-1.5 text-[13px] transition;
|
|
color: var(--text-dim);
|
|
}
|
|
.env-dialog-item:hover { background: var(--hover); color: var(--text); }
|
|
.env-dialog-item.active { background: var(--active); color: var(--accent-strong); }
|
|
.env-dialog-current {
|
|
@apply rounded-full px-1.5 py-0.5 text-[10px] font-semibold;
|
|
background: color-mix(in srgb, var(--success) 14%, transparent);
|
|
color: var(--success);
|
|
}
|
|
.env-dialog-new {
|
|
@apply mt-2 flex items-center justify-center gap-1 rounded-lg border border-dashed px-2 py-2 text-xs transition;
|
|
border-color: var(--border);
|
|
color: var(--text-dim);
|
|
}
|
|
.env-dialog-new:hover { border-color: var(--accent); color: var(--accent-strong); }
|
|
.env-dialog-main { min-width: 0; display: flex; flex-direction: column; }
|
|
.env-dialog-vars { margin-top: 10px; }
|
|
|
|
/* ---------- 信息提示条 ---------- */
|
|
.ops-hint {
|
|
@apply mb-3 flex items-start gap-2 rounded-lg px-3 py-2 text-xs leading-5;
|
|
background: color-mix(in srgb, var(--accent) 8%, transparent);
|
|
border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
|
|
color: var(--text-dim);
|
|
}
|
|
.ops-hint svg { color: var(--accent-strong); margin-top: 2px; }
|
|
.ops-hint b { color: var(--accent-strong); font-family: inherit; }
|
|
|
|
/* ---------- 设置弹窗(左侧导航 + 分区) ---------- */
|
|
.set-dialog { display: grid; grid-template-columns: 188px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); gap: 0 16px; height: min(66vh, 620px); min-height: 420px; margin: 0 -1.25rem -0.25rem; }
|
|
.set-side {
|
|
border-right: 1px solid var(--border);
|
|
padding-right: 12px;
|
|
display: flex; flex-direction: column; gap: 2px;
|
|
}
|
|
.set-side-head {
|
|
@apply mb-2 px-2 text-[11px] font-semibold uppercase tracking-[0.08em];
|
|
color: var(--text-faint);
|
|
}
|
|
.set-side-item {
|
|
@apply flex items-center gap-2.5 rounded-lg px-2 py-2 transition;
|
|
color: var(--text-dim);
|
|
}
|
|
.set-side-item:hover { background: var(--hover); color: var(--text); }
|
|
.set-side-item.active { background: var(--active); color: var(--accent-strong); }
|
|
.set-side-icon {
|
|
@apply grid h-7 w-7 shrink-0 place-items-center rounded-lg;
|
|
background: var(--panel-2);
|
|
color: var(--text-dim);
|
|
}
|
|
.set-side-icon.active { color: var(--accent-strong); }
|
|
.set-main { min-width: 0; min-height: 0; display: flex; flex-direction: column; }
|
|
.set-main-head { padding-bottom: 10px; flex-shrink: 0; }
|
|
.set-main-body { flex: 1; min-height: 0; padding-top: 14px; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; padding-right: 4px; }
|
|
.set-main-body > .set-group { flex-shrink: 0; }
|
|
.set-group {
|
|
@apply overflow-hidden rounded-xl;
|
|
border: 1px solid var(--border);
|
|
background: var(--panel-2);
|
|
}
|
|
.set-group-title {
|
|
@apply px-4 pt-3 pb-1 text-[11px] font-semibold uppercase tracking-[0.08em];
|
|
color: var(--text-faint);
|
|
}
|
|
.set-row {
|
|
@apply flex items-center gap-4 px-4 py-3;
|
|
}
|
|
.set-row + .set-row { border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent); }
|
|
.set-row-control { flex-shrink: 0; display: flex; align-items: center; gap: 6px; }
|
|
|
|
/* 开关 */
|
|
.set-toggle {
|
|
@apply relative h-[22px] w-[38px] rounded-full transition;
|
|
background: var(--border);
|
|
}
|
|
.set-toggle:hover { opacity: 0.85; }
|
|
.set-toggle.on { background: var(--accent); }
|
|
.set-toggle-dot {
|
|
@apply absolute top-[3px] left-[3px] h-4 w-4 rounded-full bg-white transition;
|
|
}
|
|
.set-toggle.on .set-toggle-dot { transform: translateX(16px); }
|
|
|
|
/* Mock 规则表(随右侧内容区整体滚动,表头吸附) */
|
|
.set-group .ops-hint { margin: 10px 12px 0; }
|
|
.mock-rules { @apply mt-2; }
|
|
.mock-rule-head, .mock-rule-row {
|
|
display: grid;
|
|
grid-template-columns: 88px 84px minmax(110px, 1fr) minmax(130px, 1.2fr) minmax(96px, 0.9fr) 44px 30px;
|
|
gap: 6px;
|
|
align-items: center;
|
|
padding: 4px 12px;
|
|
}
|
|
.mock-rule-head {
|
|
@apply sticky top-0 z-[1] text-[11px] font-semibold;
|
|
color: var(--text-faint);
|
|
background: var(--panel-2);
|
|
border-bottom: 1px solid var(--border);
|
|
padding-top: 8px;
|
|
padding-bottom: 8px;
|
|
}
|
|
.mock-rule-row { padding-top: 5px; padding-bottom: 5px; }
|
|
.mock-rule-row + .mock-rule-row { border-top: 1px solid color-mix(in srgb, var(--border) 45%, transparent); }
|
|
.mock-rule-pattern { font-family: ui-monospace, monospace; }
|
|
.mock-rule-expr { font-family: ui-monospace, monospace; color: var(--accent-strong); }
|
|
|
|
/* Mock 速查表 */
|
|
.mock-cheatsheet { @apply mt-2 grid grid-cols-2 gap-1.5; }
|
|
.mock-cheat-item {
|
|
@apply flex items-center gap-2 rounded-lg px-2.5 py-1.5 text-left text-[11px] transition;
|
|
background: var(--panel);
|
|
border: 1px solid var(--border);
|
|
color: var(--text-dim);
|
|
}
|
|
.mock-cheat-item:hover { border-color: var(--accent); color: var(--text); }
|
|
.mock-cheat-item code {
|
|
font-family: ui-monospace, monospace;
|
|
color: var(--accent-strong);
|
|
font-size: 11px;
|
|
flex-shrink: 0;
|
|
}
|
|
.mock-cheat-item .ops-faint { flex: 1; min-width: 0; @apply truncate; }
|
|
.mock-cheat-gen {
|
|
max-width: 46%;
|
|
@apply truncate font-mono text-[10px];
|
|
color: var(--text-faint);
|
|
}
|
|
.mock-preview-value {
|
|
@apply rounded-md px-2 py-1 font-mono text-[12px];
|
|
background: var(--panel-2);
|
|
border: 1px solid var(--border);
|
|
color: var(--accent-strong);
|
|
}
|
|
|
|
/* ---------- 自动化测试 ---------- */
|
|
.test-assert-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 6px;
|
|
margin-bottom: 6px;
|
|
}
|
|
.test-assert-row .ops-input { max-width: 150px; }
|
|
.test-assert-locator { font-family: ui-monospace, monospace; }
|
|
.test-assert-expected { font-family: ui-monospace, monospace; min-width: 110px; }
|
|
|
|
.test-stat-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 10px; }
|
|
.test-stat {
|
|
@apply rounded-xl px-3 py-2.5 text-center;
|
|
background: var(--panel-2);
|
|
border: 1px solid var(--border);
|
|
}
|
|
.test-stat.ok { border-color: color-mix(in srgb, var(--success) 45%, transparent); }
|
|
.test-stat.bad { border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
|
|
.test-stat-value { font-size: 20px; font-weight: 700; line-height: 1.3; }
|
|
.test-stat.ok .test-stat-value { color: var(--success); }
|
|
.test-stat.bad .test-stat-value { color: var(--danger); }
|
|
.test-stat-label { @apply mt-0.5 text-[11px]; color: var(--text-faint); }
|
|
|
|
.test-step-result {
|
|
@apply overflow-hidden rounded-xl;
|
|
background: var(--panel-2);
|
|
border: 1px solid var(--border);
|
|
}
|
|
|
|
.test-assert-table { width: 100%; border-collapse: collapse; font-size: 11px; }
|
|
.test-assert-table th {
|
|
@apply px-2 py-1 text-left font-semibold;
|
|
color: var(--text-faint);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.test-assert-table td {
|
|
@apply px-2 py-1 align-top;
|
|
border-bottom: 1px solid color-mix(in srgb, var(--border) 45%, transparent);
|
|
color: var(--text-dim);
|
|
max-width: 320px;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
/* ---------- {{变量}} 补全浮层 ---------- */
|
|
.var-hint-wrap { position: relative; min-width: 0; flex: 1; display: inline-flex; }
|
|
.var-hint-panel {
|
|
position: absolute;
|
|
top: calc(100% + 4px);
|
|
left: 0;
|
|
z-index: 40;
|
|
min-width: 280px;
|
|
max-width: 420px;
|
|
@apply overflow-hidden rounded-xl py-1 shadow-2xl;
|
|
background: var(--panel);
|
|
border: 1px solid var(--border);
|
|
}
|
|
.var-hint-head {
|
|
@apply flex items-center gap-1.5 px-2.5 py-1.5 text-[11px];
|
|
color: var(--text-dim);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.var-hint-head .ops-faint { margin-left: auto; }
|
|
.var-hint-item {
|
|
@apply flex w-full items-center gap-3 px-2.5 py-1.5 text-left font-mono text-[12px] transition;
|
|
color: var(--text-dim);
|
|
}
|
|
.var-hint-item:hover, .var-hint-item.sel { background: var(--active); color: var(--text); }
|
|
.var-hint-name { color: var(--accent-strong); flex-shrink: 0; }
|
|
.var-hint-value { @apply truncate; color: var(--text-dim); }
|
|
|
|
/* ---------- misc ---------- */
|
|
.ops-link { color: var(--accent-strong); text-decoration: none; }
|
|
.ops-link:hover { text-decoration: underline; }
|
|
.ops-selected-input {
|
|
outline: 1.5px solid var(--accent);
|
|
border-radius: 0.25rem;
|
|
}
|
|
}
|
|
|
|
@keyframes ops-pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.35; }
|
|
}
|
|
|
|
/* 拖拽落点提示 */
|
|
.ops-drop-target {
|
|
outline: 1.5px dashed var(--accent);
|
|
outline-offset: -2px;
|
|
}
|