feat: OpsxDB 桌面数据库客户端首版入库
含工作台/连库多引擎字段与 SSH·SSL、结果编辑、Redis 键浏览、 Tauri 桌面与 docs/落地页;排除本地构建产物与 sidecar 二进制。
This commit is contained in:
commit
f8740d27f7
240 files changed
+21625
No files matched your search
@@ -0,0 +1,10 @@
|
||||
import React from "react";
|
||||
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>,
|
||||
);
|
||||
@@ -0,0 +1,427 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
/* Design tokens v1 — TablePlus/DataGrip density */
|
||||
color-scheme: dark;
|
||||
--ops-bg: #0e1116;
|
||||
--ops-surface: #151a21;
|
||||
--ops-surface-2: #1a212b;
|
||||
--ops-border: #2a3340;
|
||||
--ops-border-soft: #1f2733;
|
||||
--ops-muted: #8b95a5;
|
||||
--ops-text: #e8edf5;
|
||||
--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-danger: #ef4444;
|
||||
--ops-warn: #f59e0b;
|
||||
}
|
||||
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-row-alt: #f8fafc;
|
||||
--ops-danger: #dc2626;
|
||||
--ops-warn: #d97706;
|
||||
}
|
||||
* { scrollbar-color: #334155 transparent; scrollbar-width: thin; }
|
||||
html.light * { scrollbar-color: #cbd5e1 transparent; }
|
||||
body {
|
||||
@apply m-0 font-sans antialiased;
|
||||
background: var(--ops-bg);
|
||||
color: var(--ops-text);
|
||||
}
|
||||
button, input, select, textarea { font: inherit; }
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.ops-shell {
|
||||
background: var(--ops-bg);
|
||||
color: var(--ops-text);
|
||||
}
|
||||
|
||||
.ops-input {
|
||||
@apply rounded border px-2 py-1 outline-none transition;
|
||||
font-size: var(--ops-font-sm);
|
||||
border-color: var(--ops-border);
|
||||
background: var(--ops-surface-2);
|
||||
color: var(--ops-text);
|
||||
}
|
||||
.ops-input::placeholder { color: var(--ops-muted); opacity: 0.7; }
|
||||
.ops-input:focus {
|
||||
border-color: var(--ops-accent);
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--ops-accent) 35%, transparent);
|
||||
}
|
||||
|
||||
.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;
|
||||
height: var(--ops-row-h);
|
||||
font-size: var(--ops-font-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;
|
||||
height: var(--ops-row-h);
|
||||
font-size: var(--ops-font-sm);
|
||||
border-color: var(--ops-border);
|
||||
background: var(--ops-surface-2);
|
||||
color: var(--ops-text);
|
||||
}
|
||||
.ops-btn-secondary:hover {
|
||||
border-color: color-mix(in srgb, var(--ops-accent) 45%, var(--ops-border));
|
||||
background: color-mix(in srgb, var(--ops-accent) 8%, var(--ops-surface-2));
|
||||
}
|
||||
|
||||
.ops-btn-ghost {
|
||||
@apply inline-flex items-center justify-center rounded px-1.5 transition;
|
||||
height: var(--ops-row-h);
|
||||
font-size: var(--ops-font-sm);
|
||||
color: var(--ops-muted);
|
||||
}
|
||||
.ops-btn-ghost:hover {
|
||||
color: var(--ops-text);
|
||||
background: color-mix(in srgb, var(--ops-accent) 10%, transparent);
|
||||
}
|
||||
|
||||
.ops-btn-ai {
|
||||
@apply rounded border px-1.5 py-0.5 text-[11px] font-medium transition;
|
||||
border-color: var(--ops-border);
|
||||
background: var(--ops-surface-2);
|
||||
color: var(--ops-text);
|
||||
}
|
||||
.ops-btn-ai:hover:not(:disabled) {
|
||||
border-color: color-mix(in srgb, var(--ops-accent) 50%, var(--ops-border));
|
||||
color: var(--ops-accent);
|
||||
}
|
||||
.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;
|
||||
height: var(--ops-tab-h);
|
||||
border-color: var(--ops-border);
|
||||
background: var(--ops-surface-2);
|
||||
}
|
||||
.ops-empty {
|
||||
@apply flex flex-1 flex-col items-center justify-center gap-3 px-6 text-center;
|
||||
color: var(--ops-muted);
|
||||
background: var(--ops-bg);
|
||||
}
|
||||
.ops-empty h2 {
|
||||
@apply m-0 font-semibold;
|
||||
font-size: var(--ops-font-md);
|
||||
color: var(--ops-text);
|
||||
}
|
||||
.ops-empty p {
|
||||
@apply m-0 max-w-sm;
|
||||
font-size: var(--ops-font-sm);
|
||||
}
|
||||
.ops-conn-row {
|
||||
@apply flex w-full items-center gap-3 border-b px-3 text-left transition;
|
||||
min-height: 44px;
|
||||
border-color: var(--ops-border-soft);
|
||||
background: var(--ops-surface);
|
||||
}
|
||||
.ops-conn-row:hover {
|
||||
background: color-mix(in srgb, var(--ops-accent) 8%, var(--ops-surface));
|
||||
}
|
||||
.ops-section-card {
|
||||
@apply space-y-2 rounded border p-3;
|
||||
border-color: var(--ops-border);
|
||||
background: var(--ops-surface);
|
||||
}
|
||||
.ops-section-title {
|
||||
@apply mb-1 font-semibold;
|
||||
font-size: var(--ops-font-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;
|
||||
color: var(--ops-muted);
|
||||
}
|
||||
.ops-tab:hover { color: var(--ops-text); }
|
||||
.ops-tab-active {
|
||||
border-color: var(--ops-accent);
|
||||
color: var(--ops-accent);
|
||||
}
|
||||
|
||||
.ops-code {
|
||||
@apply font-mono text-[12.5px] leading-[1.45] outline-none;
|
||||
background: #070b11;
|
||||
color: #cbd5e1;
|
||||
border-color: var(--ops-border);
|
||||
}
|
||||
html.light .ops-code {
|
||||
background: #f8fafc;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.ops-panel {
|
||||
background: var(--ops-surface);
|
||||
border-color: var(--ops-border);
|
||||
}
|
||||
|
||||
.ops-chrome-header {
|
||||
@apply flex shrink-0 items-center gap-1.5 border-b px-2.5;
|
||||
height: var(--ops-toolbar-h);
|
||||
border-color: var(--ops-border);
|
||||
background: color-mix(in srgb, var(--ops-surface) 92%, #000);
|
||||
}
|
||||
html.light .ops-chrome-header {
|
||||
background: var(--ops-surface);
|
||||
}
|
||||
|
||||
.ops-section-label {
|
||||
@apply flex h-7 shrink-0 items-center px-2.5 text-[10px] font-semibold uppercase tracking-[0.06em];
|
||||
color: var(--ops-muted);
|
||||
border-color: var(--ops-border);
|
||||
background: var(--ops-surface-2);
|
||||
}
|
||||
|
||||
.ops-statusbar {
|
||||
@apply flex shrink-0 items-center gap-2.5 border-t px-2.5;
|
||||
font-size: var(--ops-font-xs);
|
||||
height: var(--ops-statusbar-h);
|
||||
border-color: var(--ops-border);
|
||||
background: var(--ops-surface-2);
|
||||
color: var(--ops-muted);
|
||||
}
|
||||
|
||||
.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);
|
||||
color: color-mix(in srgb, var(--ops-text) 78%, var(--ops-muted));
|
||||
}
|
||||
.ops-tree-item:hover {
|
||||
background: color-mix(in srgb, var(--ops-accent) 10%, transparent);
|
||||
color: var(--ops-accent);
|
||||
}
|
||||
.ops-tree-item-active {
|
||||
background: color-mix(in srgb, var(--ops-accent) 14%, transparent);
|
||||
color: var(--ops-accent);
|
||||
}
|
||||
|
||||
.ops-grid {
|
||||
@apply w-full border-collapse text-left text-[12px];
|
||||
}
|
||||
.ops-grid th {
|
||||
@apply sticky top-0 border-b px-1.5 py-[3px] font-semibold whitespace-nowrap;
|
||||
border-color: var(--ops-border);
|
||||
background: var(--ops-surface-2);
|
||||
color: var(--ops-muted);
|
||||
}
|
||||
.ops-grid td {
|
||||
@apply border-b px-1.5 py-[2px] font-mono whitespace-nowrap;
|
||||
border-color: var(--ops-border-soft);
|
||||
color: var(--ops-text);
|
||||
}
|
||||
.ops-grid tbody tr:nth-child(even) td {
|
||||
background: var(--ops-row-alt);
|
||||
}
|
||||
.ops-grid tbody tr:hover td {
|
||||
background: color-mix(in srgb, var(--ops-accent) 8%, transparent);
|
||||
}
|
||||
.ops-grid tbody tr.ops-row-selected td {
|
||||
background: var(--ops-bg-active) !important;
|
||||
}
|
||||
.ops-grid th {
|
||||
position: relative;
|
||||
user-select: none;
|
||||
}
|
||||
.ops-grid th .ops-col-resize {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
}
|
||||
.ops-grid th.ops-sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.ops-driver-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
min-height: var(--ops-row-h);
|
||||
padding: 0 10px;
|
||||
border-bottom: 1px solid var(--ops-border-soft);
|
||||
text-align: left;
|
||||
background: transparent;
|
||||
color: var(--ops-text);
|
||||
font-size: var(--ops-font-sm);
|
||||
transition: background 0.1s;
|
||||
}
|
||||
.ops-driver-row:hover {
|
||||
background: var(--ops-bg-active);
|
||||
}
|
||||
.ops-driver-row .ops-driver-meta {
|
||||
margin-left: auto;
|
||||
font-size: var(--ops-font-xs);
|
||||
color: var(--ops-muted);
|
||||
}
|
||||
.ops-tab-dirty::before {
|
||||
content: "●";
|
||||
margin-right: 4px;
|
||||
font-size: 8px;
|
||||
color: var(--ops-accent);
|
||||
}
|
||||
.ops-grid .ops-null {
|
||||
color: var(--ops-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
.ops-grid .ops-rownum {
|
||||
@apply w-7 min-w-[1.75rem] text-right select-none;
|
||||
color: var(--ops-muted);
|
||||
background: var(--ops-surface-2) !important;
|
||||
}
|
||||
|
||||
.ops-modal {
|
||||
@apply overflow-hidden rounded-lg border;
|
||||
border-color: var(--ops-border);
|
||||
background: var(--ops-surface);
|
||||
color: var(--ops-text);
|
||||
}
|
||||
.ops-modal-header {
|
||||
@apply border-b px-3.5 py-2 text-[13px] font-semibold;
|
||||
border-color: var(--ops-border);
|
||||
background: var(--ops-surface-2);
|
||||
}
|
||||
.ops-modal-body {
|
||||
@apply px-3.5 py-2.5 text-[13px] leading-relaxed;
|
||||
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);
|
||||
}
|
||||
|
||||
.ops-card {
|
||||
@apply flex flex-col items-center gap-1.5 rounded-md border p-2.5 transition;
|
||||
border-color: var(--ops-border);
|
||||
background: color-mix(in srgb, var(--ops-surface-2) 80%, transparent);
|
||||
}
|
||||
.ops-card:hover {
|
||||
border-color: color-mix(in srgb, var(--ops-accent) 55%, var(--ops-border));
|
||||
background: color-mix(in srgb, var(--ops-accent) 8%, var(--ops-surface-2));
|
||||
}
|
||||
|
||||
details > summary .ops-tree-chevron {
|
||||
display: inline-block;
|
||||
transition: transform 0.12s ease;
|
||||
}
|
||||
details[open] > summary .ops-tree-chevron {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.ops-drop-line {
|
||||
height: 2px;
|
||||
margin: 1px 4px;
|
||||
border-radius: 1px;
|
||||
background: var(--ops-accent);
|
||||
}
|
||||
.ops-drop-slot {
|
||||
border: 1px dashed var(--ops-border);
|
||||
}
|
||||
.ops-drop-slot-active {
|
||||
border-color: var(--ops-accent);
|
||||
background: color-mix(in srgb, var(--ops-accent) 12%, transparent);
|
||||
}
|
||||
|
||||
.ops-split-x {
|
||||
width: 5px;
|
||||
cursor: col-resize;
|
||||
flex-shrink: 0;
|
||||
background: transparent;
|
||||
}
|
||||
.ops-split-x:hover,
|
||||
.ops-split-x:active {
|
||||
background: color-mix(in srgb, var(--ops-accent) 45%, transparent);
|
||||
}
|
||||
.ops-split-y {
|
||||
height: 5px;
|
||||
cursor: row-resize;
|
||||
flex-shrink: 0;
|
||||
background: transparent;
|
||||
}
|
||||
.ops-split-y:hover,
|
||||
.ops-split-y:active {
|
||||
background: color-mix(in srgb, var(--ops-accent) 45%, transparent);
|
||||
}
|
||||
.cm-editor {
|
||||
height: 100%;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ops-close-faint {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
color: color-mix(in srgb, var(--ops-muted) 55%, transparent);
|
||||
background: transparent;
|
||||
}
|
||||
.ops-close-faint:hover {
|
||||
color: var(--ops-text);
|
||||
background: color-mix(in srgb, var(--ops-muted) 18%, transparent);
|
||||
}
|
||||
.ops-ctx {
|
||||
position: fixed;
|
||||
z-index: 90;
|
||||
min-width: 140px;
|
||||
padding: 4px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--ops-border);
|
||||
background: var(--ops-surface);
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,.35);
|
||||
}
|
||||
.ops-ctx button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding: 6px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: var(--ops-font-sm);
|
||||
color: var(--ops-text);
|
||||
background: transparent;
|
||||
}
|
||||
.ops-ctx button:hover {
|
||||
background: var(--ops-bg-active);
|
||||
}
|
||||
.ops-tree-group {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--ops-muted);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
@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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user