feat: 18 引擎驱动全家桶 + 驱动管理/离线包 + 设置中心 + 工作台交互打磨
CI / Go server (push) Canceled after 0s
CI / Web + core packages (push) Canceled after 0s
CI / Desktop (Tauri) cargo check (push) Canceled after 0s

驱动接入(全部本机真实例验收):
- Elasticsearch 原生 SQL API、InfluxDB 原生 HTTP+Flux(官方 JDBC 付费许可,实测弃用)
- ZooKeeper/MongoDB JVM agent(连接时自动下载官方驱动 jar,sha256 钉版本到 ~/.opsxdb/drivers/)
- 达梦 JDBC agent 桥就绪(待厂商 jar+实例);TiDB 真实例验收
- 协议族代码审查放行 PolarDB/TDSQL/OceanBase/Kingbase/GaussDB/Oracle
- 驱动能力模型:按驱动开关对象树分组/右键菜单/行编辑,NoSQL 树拍平(对象直挂库节点)

驱动管理:
- 驱动管理弹框(状态/预装/离线包导入/JDK 检测)+ /api/drivers/* 三接口
- opsxdb-driver 驱动商店仓库(清单/fetch.sh/pack.sh 离线包/import.sh)
- 连接时懒加载:DMG 零 Java 产物,首连自动下载(实测 ZK 九件套 1.35s),离线包自动回退

UI/交互:
- 设置中心(⚙):常规/执行/结果/驱动/快捷键/关于,左导航+右面板(对标 opsxshell)
- 实例级展开箭头(行首 SVG chevron,替代字体回退失真的 Unicode 三角)
- 树内嵌套渲染修复:对象树挂在激活连接下;工具栏窄窗口不断行(按钮 nowrap+整颗换行)
- 侧栏连接行精简(图标+连接名)、SQLite SSH 守卫、Oracle 死参数清理

质量:
- Go 协议层/离线回退单测、E2E 门控测试(ES/ZK/Influx/Mongo/TiDB/JDBC 桥)
- 驱动注册表单一事实来源 connection-types.json,README/BACKLOG/AGENTS 同步
This commit is contained in:
logan committed 2026-09-22 01:07:46 +08:00
1 parent 26f459d1b8
commit a9510235bb
156 files changed
+17200 -2520

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#080d14" />
<meta name="theme-color" content="#0e1116" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>OpsxDB</title>
</head>
-2
View File
@@ -9,8 +9,6 @@
"preview": "vite preview --host 127.0.0.1 --port 5174"
},
"dependencies": {
"@opsxdb/core": "*",
"@opsxdb/transport": "*",
"@opsxdb/ui": "*",
"@tauri-apps/api": "^2.5.0",
"react": "^19.0.0",
+40 -5
View File
@@ -3,8 +3,43 @@ import ReactDOM from "react-dom/client";
import { OpsxWorkspace } from "@opsxdb/ui";
import "./styles.css";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<OpsxWorkspace />
</React.StrictMode>,
);
const root = document.getElementById("root")!;
const isTauri =
typeof window !== "undefined" &&
Boolean((window as unknown as { __TAURI_INTERNALS__?: unknown }).__TAURI_INTERNALS__);
// 产品唯一形态是桌面 Tauri:生产构建被浏览器打开时展示导引页,不渲染工作台。
// (开发态的 Vite 浏览器访问保留,仅供 UI 调试。)
if (!isTauri && import.meta.env.PROD) {
ReactDOM.createRoot(root).render(
<div
style={{
minHeight: "100vh",
display: "grid",
placeItems: "center",
background: "#0c1512",
color: "#d4d4d4",
fontFamily:
'-apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif',
}}
>
<div style={{ textAlign: "center", maxWidth: 460, padding: 24 }}>
<div style={{ fontSize: 40, marginBottom: 8 }}>🗄️</div>
<h1 style={{ fontSize: 22, margin: "0 0 10px", color: "#6ee7b7" }}>OpsxDB 请在桌面应用中打开</h1>
<p style={{ fontSize: 14, lineHeight: 1.8, color: "#8aa39b", margin: 0 }}>
客户端唯一形态是桌面 App(Tauri 2),本页面仅为桌面端构建产物。
<br />
官网与文档由本地后端提供:
<a href="http://127.0.0.1:8788/" style={{ color: "#34d399" }}>官网</a> ·{" "}
<a href="http://127.0.0.1:8788/docs" style={{ color: "#34d399" }}>文档</a>
</p>
</div>
</div>,
);
} else {
ReactDOM.createRoot(root).render(
<React.StrictMode>
<OpsxWorkspace />
</React.StrictMode>,
);
}
+193 -67
View File
@@ -4,53 +4,86 @@
@layer base {
:root {
/* Design tokens v1 — TablePlus/DataGrip density */
/* Design tokens v2 — 亮暗双套同色相,4 级字号,间距/圆角/阴影/层级全令牌化 */
color-scheme: dark;
/* 背景层级:三档,编辑器不再另起第四档深色(修 V7) */
--ops-bg: #0e1116;
--ops-surface: #151a21;
--ops-surface-2: #1a212b;
--ops-editor-bg: #10151c; /* 介于 bg 与 surface 之间,取代 #070b11 "黑洞" */
/* 边框 / 文本 */
--ops-border: #2a3340;
--ops-border-soft: #1f2733;
--ops-muted: #8b95a5;
--ops-text: #e8edf5;
--ops-muted: #8b95a5;
/* 语义色:亮暗同色相,仅调明度(修 V3) */
--ops-accent: #3b82f6;
--ops-accent-strong: #2563eb;
--ops-row-alt: rgba(26, 33, 43, 0.65);
--ops-toolbar-h: 36px;
--ops-tab-h: 32px;
--ops-row-h: 28px;
--ops-statusbar-h: 24px;
--ops-sidebar-w: 260px;
--ops-font-xs: 11px;
--ops-font-sm: 12.5px;
--ops-font-md: 14px;
--ops-bg-active: color-mix(in srgb, var(--ops-accent) 16%, transparent);
--ops-accent-soft: color-mix(in srgb, var(--ops-accent) 16%, transparent); /* 补齐 V1 */
--ops-danger: #ef4444;
--ops-warn: #f59e0b;
--ops-warning: #f59e0b; /* 统一命名,已删除 --ops-warn 别名(修 V1,引用侧由 WS-C 收敛) */
--ops-success: #10b981;
/* 兼容别名:--ops-fg 是历史误引用,统一收敛为 --ops-text(修 V1) */
--ops-fg: var(--ops-text);
--ops-input-bg: var(--ops-surface-2); /* 补齐 V1 */
--ops-row-alt: rgba(26, 33, 43, 0.65);
--ops-bg-active: var(--ops-accent-soft);
/* 字号:收敛为 4 级(修 V4,废除 10/11.5/12.5px) */
--ops-text-xs: 11px; /* 徽章、分组标签、状态栏 */
--ops-text-sm: 12px; /* 网格、树、次要文本 */
--ops-text-md: 13px; /* 正文、模态、表单 */
--ops-text-lg: 15px; /* 模态标题、空态标题 */
/* v1 字号别名:映射到 4 级新令牌,WS-C 收敛引用后删除 */
--ops-font-xs: var(--ops-text-xs);
--ops-font-sm: var(--ops-text-sm);
--ops-font-md: var(--ops-text-md);
/* 间距:4 的倍数 */
--ops-space-1: 4px; --ops-space-2: 8px; --ops-space-3: 12px;
--ops-space-4: 16px; --ops-space-5: 24px; --ops-space-6: 32px;
/* 圆角:3 级(修 V5) */
--ops-radius-sm: 4px; /* 按钮、输入框、标签 */
--ops-radius-md: 6px; /* 卡片、右键菜单 */
--ops-radius-lg: 10px; /* 模态、Toast */
/* 阴影:2 级 */
--ops-shadow-pop: 0 8px 24px rgb(0 0 0 / .35);
--ops-shadow-modal: 0 16px 48px rgb(0 0 0 / .45);
/* 层级 */
--ops-z-banner: 40; /* 断连/后端未就绪横幅 */
--ops-z-ctx: 80; /* 右键菜单 */
--ops-z-toast: 90;
--ops-z-modal: 100;
--ops-z-confirm: 110;
/* 尺寸(保留 v1) */
--ops-toolbar-h: 36px; --ops-tab-h: 32px; --ops-row-h: 28px;
--ops-statusbar-h: 24px; --ops-sidebar-w: 260px;
/* 滚动条走令牌(修 V11) */
--ops-scrollbar: #334155;
}
html.light {
/* 亮色:同色相,仅调明度 */
color-scheme: light;
--ops-bg: #f1f5f9;
--ops-surface: #ffffff;
--ops-surface-2: #f8fafc;
--ops-border: #e2e8f0;
--ops-border-soft: #eef2f7;
--ops-muted: #64748b;
--ops-text: #1e293b;
--ops-accent: #0891b2;
--ops-accent-strong: #0e7490;
--ops-bg: #f1f5f9; --ops-surface: #ffffff; --ops-surface-2: #f8fafc;
--ops-editor-bg: #f1f5f9;
--ops-border: #e2e8f0; --ops-border-soft: #eef2f7;
--ops-text: #1e293b; --ops-muted: #64748b;
--ops-accent: #2563eb; /* 蓝色系,不再切换青色(修 V3) */
--ops-accent-strong: #1d4ed8;
--ops-danger: #dc2626; --ops-warning: #d97706; --ops-success: #059669;
--ops-row-alt: #f8fafc;
--ops-danger: #dc2626;
--ops-warn: #d97706;
--ops-scrollbar: #cbd5e1;
}
* { scrollbar-color: #334155 transparent; scrollbar-width: thin; }
html.light * { scrollbar-color: #cbd5e1 transparent; }
* { scrollbar-color: var(--ops-scrollbar) transparent; scrollbar-width: thin; }
body {
@apply m-0 font-sans antialiased;
background: var(--ops-bg);
color: var(--ops-text);
}
button, input, select, textarea { font: inherit; }
/* 全局焦点环:覆盖按钮/树项/Tab(02-P2-3) */
:focus-visible {
outline: 2px solid var(--ops-accent);
outline-offset: 1px;
}
}
@layer components {
@@ -61,9 +94,9 @@
.ops-input {
@apply rounded border px-2 py-1 outline-none transition;
font-size: var(--ops-font-sm);
font-size: var(--ops-text-sm);
border-color: var(--ops-border);
background: var(--ops-surface-2);
background: var(--ops-input-bg);
color: var(--ops-text);
}
.ops-input::placeholder { color: var(--ops-muted); opacity: 0.7; }
@@ -73,17 +106,28 @@
}
.ops-btn-primary {
@apply inline-flex items-center justify-center rounded px-2.5 font-semibold text-white shadow-sm transition disabled:cursor-not-allowed disabled:opacity-50;
@apply inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded px-2.5 font-semibold text-white shadow-sm transition disabled:cursor-not-allowed disabled:opacity-50;
height: var(--ops-row-h);
font-size: var(--ops-font-sm);
font-size: var(--ops-text-sm);
background: var(--ops-accent-strong);
}
.ops-btn-primary:hover:not(:disabled) { background: var(--ops-accent); }
.ops-btn-secondary {
@apply inline-flex items-center justify-center rounded border px-2 transition;
/* 危险主按钮:删除/截断/无 WHERE 更新等确认场景统一红钮(修 C2/C3、G5) */
.ops-btn-danger {
@apply inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded px-2.5 font-semibold text-white shadow-sm transition disabled:cursor-not-allowed disabled:opacity-50;
height: var(--ops-row-h);
font-size: var(--ops-font-sm);
font-size: var(--ops-text-sm);
background: var(--ops-danger);
}
.ops-btn-danger:hover:not(:disabled) {
background: color-mix(in srgb, var(--ops-danger) 85%, #fff);
}
.ops-btn-secondary {
@apply inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded border px-2 transition;
height: var(--ops-row-h);
font-size: var(--ops-text-sm);
border-color: var(--ops-border);
background: var(--ops-surface-2);
color: var(--ops-text);
@@ -94,9 +138,9 @@
}
.ops-btn-ghost {
@apply inline-flex items-center justify-center rounded px-1.5 transition;
@apply inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded px-1.5 transition;
height: var(--ops-row-h);
font-size: var(--ops-font-sm);
font-size: var(--ops-text-sm);
color: var(--ops-muted);
}
.ops-btn-ghost:hover {
@@ -105,7 +149,8 @@
}
.ops-btn-ai {
@apply rounded border px-1.5 py-0.5 text-[11px] font-medium transition;
@apply rounded border px-1.5 py-0.5 font-medium transition;
font-size: var(--ops-text-xs);
border-color: var(--ops-border);
background: var(--ops-surface-2);
color: var(--ops-text);
@@ -117,7 +162,7 @@
.ops-btn-ai:disabled { opacity: 0.5; cursor: not-allowed; }
.ops-tabbar {
@apply flex shrink-0 items-stretch gap-0.5 border-b px-1;
@apply flex shrink-0 items-stretch gap-0.5 overflow-x-auto border-b px-1;
height: var(--ops-tab-h);
border-color: var(--ops-border);
background: var(--ops-surface-2);
@@ -129,12 +174,12 @@
}
.ops-empty h2 {
@apply m-0 font-semibold;
font-size: var(--ops-font-md);
font-size: var(--ops-text-lg);
color: var(--ops-text);
}
.ops-empty p {
@apply m-0 max-w-sm;
font-size: var(--ops-font-sm);
font-size: var(--ops-text-sm);
}
.ops-conn-row {
@apply flex w-full items-center gap-3 border-b px-3 text-left transition;
@@ -151,14 +196,14 @@
background: var(--ops-surface);
}
.ops-section-title {
@apply mb-1 font-semibold;
font-size: var(--ops-font-xs);
@apply mb-1 font-semibold uppercase;
font-size: var(--ops-text-xs);
color: var(--ops-muted);
letter-spacing: 0.04em;
text-transform: uppercase;
}
.ops-tab {
@apply border-b-2 border-transparent px-2.5 py-1.5 text-[12px] font-medium transition;
@apply border-b-2 border-transparent px-2.5 py-1.5 font-medium transition;
font-size: var(--ops-text-sm);
color: var(--ops-muted);
}
.ops-tab:hover { color: var(--ops-text); }
@@ -167,16 +212,14 @@
color: var(--ops-accent);
}
/* 编辑器:背景走 --ops-editor-bg,不再另起深色档(修 V7) */
.ops-code {
@apply font-mono text-[12.5px] leading-[1.45] outline-none;
background: #070b11;
color: #cbd5e1;
@apply font-mono leading-[1.45] outline-none;
font-size: var(--ops-text-sm);
background: var(--ops-editor-bg);
color: var(--ops-text);
border-color: var(--ops-border);
}
html.light .ops-code {
background: #f8fafc;
color: #334155;
}
.ops-panel {
background: var(--ops-surface);
@@ -194,7 +237,8 @@
}
.ops-section-label {
@apply flex h-7 shrink-0 items-center px-2.5 text-[10px] font-semibold uppercase tracking-[0.06em];
@apply flex h-7 shrink-0 items-center px-2.5 font-semibold uppercase tracking-[0.06em];
font-size: var(--ops-text-xs);
color: var(--ops-muted);
border-color: var(--ops-border);
background: var(--ops-surface-2);
@@ -202,7 +246,7 @@
.ops-statusbar {
@apply flex shrink-0 items-center gap-2.5 border-t px-2.5;
font-size: var(--ops-font-xs);
font-size: var(--ops-text-xs);
height: var(--ops-statusbar-h);
border-color: var(--ops-border);
background: var(--ops-surface-2);
@@ -212,7 +256,7 @@
.ops-tree-item {
@apply flex w-full items-center gap-1 truncate rounded px-1.5 text-left leading-tight transition;
min-height: var(--ops-row-h);
font-size: var(--ops-font-sm);
font-size: var(--ops-text-sm);
color: color-mix(in srgb, var(--ops-text) 78%, var(--ops-muted));
}
.ops-tree-item:hover {
@@ -225,7 +269,8 @@
}
.ops-grid {
@apply w-full border-collapse text-left text-[12px];
@apply w-full border-collapse text-left;
font-size: var(--ops-text-sm);
}
.ops-grid th {
@apply sticky top-0 border-b px-1.5 py-[3px] font-semibold whitespace-nowrap;
@@ -273,7 +318,7 @@
text-align: left;
background: transparent;
color: var(--ops-text);
font-size: var(--ops-font-sm);
font-size: var(--ops-text-sm);
transition: background 0.1s;
}
.ops-driver-row:hover {
@@ -281,7 +326,7 @@
}
.ops-driver-row .ops-driver-meta {
margin-left: auto;
font-size: var(--ops-font-xs);
font-size: var(--ops-text-xs);
color: var(--ops-muted);
}
.ops-tab-dirty::before {
@@ -300,25 +345,64 @@
background: var(--ops-surface-2) !important;
}
/* 统一模态基件样式:圆角/阴影/字级全走令牌,页脚右对齐 */
.ops-modal-overlay {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
padding: var(--ops-space-4);
background: rgb(0 0 0 / .55);
backdrop-filter: blur(2px);
}
.ops-modal {
@apply overflow-hidden rounded-lg border;
@apply overflow-hidden border;
border-radius: var(--ops-radius-lg);
border-color: var(--ops-border);
background: var(--ops-surface);
color: var(--ops-text);
box-shadow: var(--ops-shadow-modal);
font-size: var(--ops-text-md);
}
.ops-modal-header {
@apply border-b px-3.5 py-2 text-[13px] font-semibold;
@apply border-b px-3.5 py-2 font-semibold;
font-size: var(--ops-text-md);
border-color: var(--ops-border);
background: var(--ops-surface-2);
}
.ops-modal-body {
@apply px-3.5 py-2.5 text-[13px] leading-relaxed;
@apply px-3.5 py-2.5 leading-relaxed;
font-size: var(--ops-text-md);
color: color-mix(in srgb, var(--ops-text) 90%, var(--ops-muted));
}
.ops-modal-footer {
@apply flex justify-end gap-2 border-t px-3.5 py-2;
border-color: var(--ops-border);
}
/* 危险确认语句清单(前 3 条 + 折叠,修 C2) */
.ops-modal-items {
@apply mt-2 space-y-1 rounded border px-2.5 py-2 font-mono whitespace-pre-wrap break-all;
font-size: var(--ops-text-xs);
border-color: var(--ops-border-soft);
background: var(--ops-editor-bg);
color: var(--ops-muted);
}
/* Toast:圆角/阴影走令牌,分级色由调用侧以 var(--ops-*) 注入 */
.ops-toast {
pointer-events: auto;
display: flex;
width: 360px;
max-width: calc(100vw - 2rem);
align-items: flex-start;
gap: var(--ops-space-3);
padding: var(--ops-space-3) var(--ops-space-4);
border: 1px solid var(--ops-border);
border-radius: var(--ops-radius-lg);
background: var(--ops-surface);
box-shadow: var(--ops-shadow-pop);
}
.ops-card {
@apply flex flex-col items-center gap-1.5 rounded-md border p-2.5 transition;
@@ -330,6 +414,36 @@
background: color-mix(in srgb, var(--ops-accent) 8%, var(--ops-surface-2));
}
/* 结果区分段控件(02-§4.2-5,供 ResultPane 内部 Tab 使用) */
.ops-seg {
@apply inline-flex items-center gap-0.5 rounded border p-0.5;
border-color: var(--ops-border);
background: var(--ops-surface-2);
}
.ops-seg button {
@apply rounded px-2 py-0.5 transition;
font-size: var(--ops-text-xs);
color: var(--ops-muted);
background: transparent;
}
.ops-seg button:hover { color: var(--ops-text); }
.ops-seg button.ops-seg-active {
background: var(--ops-surface);
color: var(--ops-text);
box-shadow: 0 1px 2px rgb(0 0 0 / .25);
}
/* 骨架屏基类(备用):低饱和呼吸动画 */
.ops-skel {
border-radius: var(--ops-radius-sm);
background: color-mix(in srgb, var(--ops-muted) 18%, transparent);
animation: ops-skel-pulse 1.6s ease-in-out infinite;
}
@keyframes ops-skel-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.45; }
}
details > summary .ops-tree-chevron {
display: inline-block;
transition: transform 0.12s ease;
@@ -380,10 +494,12 @@
background: transparent;
}
/* Tab 关闭按钮:固定 18px 占位,透明度切换消除横向抖动(修 T1) */
.ops-close-faint {
display: inline-flex;
align-items: center;
justify-content: center;
flex: none;
width: 18px;
height: 18px;
border-radius: 3px;
@@ -396,31 +512,41 @@
color: var(--ops-text);
background: color-mix(in srgb, var(--ops-muted) 18%, transparent);
}
/* 右键菜单:z-index/阴影/圆角走令牌 */
.ops-ctx {
position: fixed;
z-index: 90;
z-index: var(--ops-z-ctx);
min-width: 140px;
padding: 4px;
border-radius: 6px;
padding: var(--ops-space-1);
border-radius: var(--ops-radius-md);
border: 1px solid var(--ops-border);
background: var(--ops-surface);
box-shadow: 0 8px 24px rgba(0,0,0,.35);
box-shadow: var(--ops-shadow-pop);
}
.ops-ctx button {
display: block;
width: 100%;
text-align: left;
padding: 6px 10px;
border-radius: 4px;
font-size: var(--ops-font-sm);
border-radius: var(--ops-radius-sm);
font-size: var(--ops-text-sm);
color: var(--ops-text);
background: transparent;
}
.ops-ctx button:hover {
.ops-ctx button:hover:not(:disabled) {
background: var(--ops-bg-active);
}
.ops-ctx button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.ops-ctx-sep {
height: 1px;
margin: 4px 6px;
background: var(--ops-border);
}
.ops-tree-group {
font-size: 10px;
font-size: var(--ops-text-xs);
font-weight: 600;
letter-spacing: 0.04em;
color: var(--ops-muted);
-48
View File
@@ -1,48 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root { color-scheme: dark; }
html.light { color-scheme: light; }
* { scrollbar-color: #334155 transparent; scrollbar-width: thin; }
body { @apply m-0 bg-[#080d14] font-sans antialiased text-slate-200; }
html.light body { @apply bg-slate-50 text-slate-800; }
button, input, select, textarea { font: inherit; }
}
@layer components {
.ops-input {
@apply rounded-md border border-slate-700 bg-slate-900 px-3 py-2 text-sm text-slate-200 outline-none transition placeholder:text-slate-600 focus:border-cyan-500 focus:ring-1 focus:ring-cyan-500/30;
}
html.light .ops-input {
@apply border-slate-300 bg-white text-slate-800 placeholder:text-slate-400;
}
.ops-btn-primary {
@apply rounded-md bg-cyan-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-cyan-500 disabled:cursor-not-allowed disabled:opacity-50;
}
.ops-btn-secondary {
@apply rounded-md border border-slate-700 bg-slate-900 px-3 py-1.5 text-sm text-slate-300 transition hover:border-slate-600 hover:bg-slate-800;
}
html.light .ops-btn-secondary {
@apply border-slate-300 bg-white text-slate-700 hover:bg-slate-100;
}
.ops-btn-ai {
@apply rounded-md border border-slate-700 bg-slate-900/80 px-2.5 py-1.5 text-xs font-medium text-slate-300 transition hover:border-cyan-600 hover:bg-cyan-950/40 hover:text-cyan-300;
}
.ops-tab {
@apply border-b-2 border-transparent px-4 py-3 text-xs font-medium text-slate-500 transition hover:text-slate-200;
}
.ops-tab-active {
@apply border-cyan-500 text-cyan-300;
}
.ops-code {
@apply rounded-md border border-slate-800 bg-[#070b11] p-3 font-mono text-xs leading-5 text-slate-300 outline-none focus:border-cyan-700;
}
.ops-panel {
@apply border-slate-800 bg-slate-950/80;
}
html.light .ops-panel {
@apply border-slate-200 bg-white;
}
}
+59 -1
View File
@@ -1,5 +1,9 @@
import type { Config } from "tailwindcss";
/**
* 设计令牌 v2 映射:与 apps/web/src/styles.css 的 :root 令牌同源,
* 新增组件样式优先使用 ops-* utility,避免散落硬编码色值。
*/
export default {
content: [
"./index.html",
@@ -8,7 +12,61 @@ export default {
],
theme: {
extend: {
fontFamily: { mono: ["JetBrains Mono", "SFMono-Regular", "Menlo", "monospace"] },
colors: {
ops: {
bg: "var(--ops-bg)",
surface: "var(--ops-surface)",
"surface-2": "var(--ops-surface-2)",
"editor-bg": "var(--ops-editor-bg)",
border: "var(--ops-border)",
"border-soft": "var(--ops-border-soft)",
text: "var(--ops-text)",
muted: "var(--ops-muted)",
accent: "var(--ops-accent)",
"accent-strong": "var(--ops-accent-strong)",
"accent-soft": "var(--ops-accent-soft)",
danger: "var(--ops-danger)",
warning: "var(--ops-warning)",
success: "var(--ops-success)",
"input-bg": "var(--ops-input-bg)",
"row-alt": "var(--ops-row-alt)",
"bg-active": "var(--ops-bg-active)",
},
},
fontSize: {
"ops-xs": "var(--ops-text-xs)",
"ops-sm": "var(--ops-text-sm)",
"ops-md": "var(--ops-text-md)",
"ops-lg": "var(--ops-text-lg)",
},
spacing: {
"ops-1": "var(--ops-space-1)",
"ops-2": "var(--ops-space-2)",
"ops-3": "var(--ops-space-3)",
"ops-4": "var(--ops-space-4)",
"ops-5": "var(--ops-space-5)",
"ops-6": "var(--ops-space-6)",
},
borderRadius: {
"ops-sm": "var(--ops-radius-sm)",
"ops-md": "var(--ops-radius-md)",
"ops-lg": "var(--ops-radius-lg)",
},
boxShadow: {
"ops-pop": "var(--ops-shadow-pop)",
"ops-modal": "var(--ops-shadow-modal)",
},
zIndex: {
"ops-banner": "var(--ops-z-banner)",
"ops-ctx": "var(--ops-z-ctx)",
"ops-toast": "var(--ops-z-toast)",
"ops-modal": "var(--ops-z-modal)",
"ops-confirm": "var(--ops-z-confirm)",
},
// 修 V6:删除 JetBrains Mono 声明,等宽栈与 SqlEditor 统一为系统栈
fontFamily: {
mono: ["ui-monospace", "SFMono-Regular", "Menlo", "Consolas", "monospace"],
},
},
},
plugins: [],
+1 -4
View File
@@ -5,10 +5,7 @@ import path from "node:path";
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@opsxdb/core": path.resolve(__dirname, "../../packages/core/src/index.ts"),
"@opsxdb/transport": path.resolve(__dirname, "../../packages/transport/src/index.ts"),
"@opsxdb/ui": path.resolve(__dirname, "../../packages/ui/src/index.ts"),
alias: { "@opsxdb/ui": path.resolve(__dirname, "../../packages/ui/src/index.ts"),
},
},
server: {