feat: 18 引擎驱动全家桶 + 驱动管理/离线包 + 设置中心 + 工作台交互打磨
驱动接入(全部本机真实例验收): - 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:
1 parent
26f459d1b8
commit
a9510235bb
156 files changed
+17200
-2520
No files matched your search
+1
-1
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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: [],
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in new issue
Block a user