@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; }