🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

navidog

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

navidog - npm Package Compare versions

Comparing version
1.2.2
to
1.2.5
+1
dist/assets/index-DAqpsxv-.css
:root{--bg-app: #ececec;--bg-toolbar: #e8e8e8;--bg-sidebar: #f5f5f5;--bg-main: #ffffff;--bg-info: #fafafa;--bg-statusbar: #e8e8e8;--bg-modal-overlay: rgba(0, 0, 0, .35);--border: #d4d4d4;--border-light: #e6e6e6;--border-dark: #bbb;--select-bg: #2968c8;--select-bg-hover: #e8f0fe;--select-text: #ffffff;--text: #333333;--text-secondary: #777777;--text-muted: #aaaaaa;--accent: #2968c8;--accent-hover: #1a54b0;--danger: #d93025;--success: #1e8e3e;--warning: #f9a825;--table-header-bg: #f0f0f0;--table-row-alt: #f9f9fc;--table-row-hover: #eef3fb;--table-border: #e2e2e2;--tab-active-bg: #ffffff;--tab-inactive-bg: #e8e8e8;--tab-border: #cccccc;--toolbar-h: 72px;--statusbar-h: 26px;--sidebar-w: 240px;--info-w: 360px;--tab-h: 32px;--font-sans: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", "Segoe UI", sans-serif;--font-mono: "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", monospace;--font-size: 13px;--font-size-sm: 13px;--font-size-xs: 12px}[data-theme=dark]{--bg-app: #1e1e1e;--bg-toolbar: #252526;--bg-sidebar: #1e1e1e;--bg-main: #1e1e1e;--bg-info: #252526;--bg-statusbar: #1e1e1e;--bg-modal-overlay: rgba(0, 0, 0, .6);--border: #3c3c3c;--border-light: #2d2d2d;--border-dark: #4a4a4a;--select-bg: #264f78;--select-bg-hover: #2a2d2e;--select-text: #ffffff;--text: #cccccc;--text-secondary: #969696;--text-muted: #5a5a5a;--accent: #0078d4;--accent-hover: #1a8aed;--danger: #f44747;--success: #4ec9b0;--warning: #cca700;--table-header-bg: #2d2d2d;--table-row-alt: #232323;--table-row-hover: #2a2d2e;--table-border: #3c3c3c;--tab-active-bg: #1e1e1e;--tab-inactive-bg: #2d2d2d;--tab-border: #3c3c3c;color-scheme:dark}[data-theme=dark] input,[data-theme=dark] select,[data-theme=dark] textarea{background:#2d2d2d;color:#ccc;border-color:#3c3c3c}[data-theme=dark] .history-panel,[data-theme=dark] .combo-box-dropdown{background:#252526}[data-theme=dark] .cli-container{background:#1b1b1b}[data-theme=dark] .cli-output{color:#ccc}[data-theme=dark] .cli-welcome{color:#5a5a5a;border-bottom-color:#3c3c3c}[data-theme=dark] .cli-result{color:#969696}[data-theme=dark] .import-error-list{background:#3a2020;border-color:#5a3030}[data-theme=dark] .toolbar-btn:hover{background:#ffffff14}[data-theme=dark] .toolbar-btn:active{background:#ffffff1f}[data-theme=dark] .toolbar-btn.active{background:#264f78;color:#6cb6ff}*,*:before,*:after{box-sizing:border-box}body{margin:0;font-family:var(--font-sans);font-size:var(--font-size);color:var(--text);background:var(--bg-app);overflow:hidden}button,input,textarea,select{font:inherit}button{cursor:pointer;border:none;background:none}ul,ol{list-style:none;margin:0;padding:0}#root{height:100vh}.app-shell{display:flex;flex-direction:column;height:100vh;overflow:hidden}.toolbar{height:var(--toolbar-h);background:var(--bg-toolbar);border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 12px;gap:2px;flex-shrink:0;-webkit-user-select:none;user-select:none}.toolbar-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;padding:6px 12px;border-radius:6px;color:var(--text);font-size:var(--font-size-xs);min-width:56px;transition:background .12s}.toolbar-btn:hover{background:#0000000f}.toolbar-btn:active{background:#0000001a}.toolbar-btn.active{background:#e8f0fe;color:#1a73e8}.toolbar-btn .tb-icon{font-size:24px;line-height:1;display:flex;align-items:center;justify-content:center;width:32px;height:32px}.toolbar-btn .tb-label{white-space:nowrap}.toolbar-sep{width:1px;height:44px;background:var(--border);margin:0 6px;flex-shrink:0}.toolbar-spacer{flex:1}.workspace{flex:1;display:flex;overflow:hidden;min-height:0}.sidebar{width:var(--sidebar-w);min-width:140px;background:var(--bg-sidebar);border-right:none;display:flex;flex-direction:column;flex-shrink:0;overflow:hidden}.panel-resize-handle.vertical{width:5px;cursor:col-resize;background:var(--border);flex-shrink:0;position:relative;transition:background .15s;z-index:5;align-self:stretch}.panel-resize-handle.vertical:before{content:"";position:absolute;inset:0 -3px;z-index:6}.panel-resize-handle.vertical:hover,.panel-resize-handle.vertical:active{background:var(--accent)}.panel-resize-handle.vertical:after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);height:30px;width:2px;border-left:1px solid rgba(0,0,0,.15);border-right:1px solid rgba(0,0,0,.15)}.sidebar-header{padding:8px 12px 6px;font-size:var(--font-size-sm);font-weight:600;color:var(--text-secondary);border-bottom:1px solid var(--border-light);display:flex;align-items:center;gap:6px;flex-shrink:0}.search-with-history-wrap{position:relative;flex:none;max-width:100%}.sidebar-search-wrap{margin:6px 8px;align-self:stretch}.sidebar-search{margin:0;padding:4px 8px;border:1px solid var(--border);border-radius:4px;font-size:var(--font-size-sm);background:#fff;outline:none;flex-shrink:0;width:100%}.sidebar-search:focus{border-color:var(--accent)}.sidebar-search::placeholder{color:var(--text-muted)}.search-history-dropdown{position:absolute;top:100%;left:0;right:0;z-index:100;background:#fff;border:1px solid var(--border);border-radius:6px;box-shadow:0 4px 12px #0000001f;margin-top:2px;overflow:hidden}.search-history-item{padding:6px 10px;font-size:12px;color:var(--text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.search-history-item:hover{background:var(--table-row-hover)}[data-theme=dark] .search-history-dropdown{background:#21262d;border-color:#30363d}.tree-container{flex:1;overflow-y:auto;overflow-x:hidden;padding:2px 0}.tree-node{display:flex;align-items:center;padding:2px 8px 2px 0;cursor:pointer;font-size:var(--font-size-sm);white-space:nowrap;-webkit-user-select:none;user-select:none;border:1px solid transparent;min-height:24px}.tree-node:hover{background:var(--select-bg-hover)}.tree-node.connecting{background:linear-gradient(90deg,rgba(41,104,200,.12),transparent 70%);border-color:#2968c81f}.tree-node.selected{background:var(--select-bg);color:var(--select-text)}.tree-node.selected:hover{background:var(--accent-hover)}.tree-arrow{width:16px;height:16px;display:flex;align-items:center;justify-content:center;font-size:10px;color:var(--text-secondary);flex-shrink:0;transition:transform .12s ease}.tree-arrow.expanded{transform:rotate(90deg)}.tree-node.selected .tree-arrow{color:var(--select-text)}.conn-color-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.tree-icon{width:18px;height:18px;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-right:4px;font-size:14px}.tree-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}.tree-connection-dot{width:14px;height:14px;border-radius:50%;border:1px solid #d4dae3;background:radial-gradient(circle at 35% 35%,#fff,#f5f7fb 38%,#d9dee7);box-shadow:inset 0 1px 1px #ffffffe6}.tree-connection-dot.online{border-color:#34c75961;background:radial-gradient(circle at 35% 35%,#b7f7b0,#63d85f 40%,#27a844);box-shadow:0 0 10px #34c75947}.tree-connection-dot.connecting{border-width:2px;border-style:solid;border-color:#2968c82e;border-top-color:var(--accent);border-right-color:var(--accent);background:#fffffff2;box-shadow:0 0 0 2px #2968c814;animation:spin-ring .78s linear infinite}.tree-activity-badge{flex-shrink:0;margin-left:8px;padding:2px 8px;border-radius:999px;border:1px solid rgba(41,104,200,.2);background:#2968c81f;color:#1f5ec7;font-size:10px;font-weight:700}.tree-node.selected .tree-activity-badge{color:#fffffff5;background:#ffffff29;border-color:#ffffff47}.main-area{flex:1;display:flex;flex-direction:column;min-width:0;background:var(--bg-main);overflow:hidden}.tab-bar{display:flex;align-items:flex-end;background:var(--tab-inactive-bg);border-bottom:1px solid var(--border);flex-shrink:0;min-height:var(--tab-h);overflow-x:auto;overflow-y:hidden;-webkit-user-select:none;user-select:none}.tab-bar::-webkit-scrollbar{height:0}.tab-item{display:flex;align-items:center;gap:6px;padding:6px 12px;font-size:var(--font-size-sm);border:1px solid var(--tab-border);border-bottom:none;border-radius:4px 4px 0 0;background:var(--tab-inactive-bg);color:var(--text-secondary);margin-left:-1px;cursor:pointer;white-space:nowrap;max-width:200px;position:relative;transition:background .1s}.tab-item:first-child{margin-left:4px}.tab-item:hover{background:#f0f0f0}.tab-item.active{background:var(--tab-active-bg);color:var(--text);border-bottom:1px solid var(--tab-active-bg);z-index:1;font-weight:500}.tab-item .tab-icon{font-size:14px}.tab-item .tab-title{overflow:hidden;text-overflow:ellipsis}.tab-close{width:16px;height:16px;display:flex;align-items:center;justify-content:center;border-radius:3px;font-size:14px;color:var(--text-muted);flex-shrink:0;line-height:1}.tab-close:hover{background:#0000001a;color:var(--text)}.tab-item.active .tab-close{color:var(--text-secondary)}.tab-content{flex:1;display:flex;flex-direction:column;overflow:hidden;min-height:0}.empty-state{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;color:var(--text-muted);font-size:14px}.empty-state .empty-icon{font-size:48px;opacity:.4}.empty-state .empty-title{font-size:16px;color:var(--text-secondary);font-weight:500}.objects-toolbar{display:flex;align-items:center;padding:6px 8px;gap:6px;border-bottom:1px solid var(--border-light);flex-shrink:0}.objects-toolbar button{padding:3px 6px;border-radius:3px;font-size:var(--font-size-sm);color:var(--text-secondary)}.objects-toolbar button:hover{background:#0000000f}.objects-search-wrap{width:180px;flex-shrink:0}.objects-search{padding:3px 8px;border:1px solid var(--border);border-radius:4px;font-size:var(--font-size-sm);width:100%;background:#fff;outline:none;margin-left:0}.objects-search:focus{border-color:var(--accent)}.objects-view-toggle{display:flex;gap:2px}.view-btn{padding:4px 6px!important;font-size:14px!important}.view-btn.active{background:#00000014!important}.objects-grid{flex:1;overflow:auto}.objects-table{width:100%;border-collapse:collapse}.objects-table th{position:sticky;top:0;background:var(--table-header-bg);border-bottom:1px solid var(--table-border);padding:5px 12px;text-align:left;font-size:var(--font-size-sm);font-weight:500;color:var(--text-secondary);white-space:nowrap;z-index:1}.objects-table td{padding:4px 12px;border-bottom:1px solid var(--border-light);font-size:var(--font-size-sm);white-space:nowrap}.objects-table tr:hover td{background:var(--table-row-hover)}.objects-table .obj-icon{margin-right:6px;font-size:14px}.data-toolbar{display:flex;align-items:center;padding:4px 8px;gap:4px;border-bottom:1px solid var(--border-light);flex-shrink:0;background:var(--bg-info)}.data-toolbar button{padding:3px 8px;border-radius:3px;font-size:var(--font-size-sm);color:var(--text-secondary)}.data-toolbar button:hover{background:#0000000f}.filter-label{font-size:var(--font-size-xs);color:var(--text-secondary);white-space:nowrap}.filter-input{flex:1;max-width:300px;min-width:120px;padding:2px 8px;border:1px solid var(--border);border-radius:4px;font-size:var(--font-size-xs);font-family:var(--font-mono);outline:none;transition:border-color .15s}.filter-input:focus{border-color:var(--accent)}.filter-clear{background:none;border:none;color:var(--danger);cursor:pointer;font-size:12px;padding:2px 4px;border-radius:3px}.filter-clear:hover{background:#fee}.sort-indicator{font-size:11px;color:var(--accent);font-weight:700}.col-header{position:relative;cursor:default}.col-header-label{flex:1}.col-sort-trigger{opacity:0;font-size:10px;color:var(--fg-dim);cursor:pointer;padding:0 2px;transition:opacity .15s;-webkit-user-select:none;user-select:none}.col-header:hover .col-sort-trigger{opacity:1}.col-sort-menu{position:absolute;top:100%;left:0;z-index:200;background:#fff;border:1px solid var(--border);border-radius:6px;box-shadow:0 4px 16px #00000038;min-width:140px;padding:4px 0;font-size:13px;font-weight:400;white-space:nowrap}.col-sort-item{padding:6px 12px;cursor:pointer;display:flex;align-items:center;gap:2px}.col-sort-item:hover{background:var(--hover)}.col-sort-sep{height:1px;background:var(--border);margin:4px 0}.filter-tag{display:inline-flex;align-items:center;gap:4px;background:#e8f0fe;color:#1a73e8;font-size:12px;padding:2px 8px;border-radius:12px;margin-right:4px;white-space:nowrap}.filter-tag strong{font-weight:600}.filter-tag-close{background:none;border:none;color:#1a73e8;cursor:pointer;font-size:11px;padding:0 2px;line-height:1}.filter-tag-close:hover{color:#c62828}.filter-clear-all{background:none;border:1px solid var(--border);border-radius:4px;color:var(--fg-dim);font-size:11px;cursor:pointer;padding:2px 8px;margin-left:4px}.filter-clear-all:hover{background:var(--hover)}.toolbar-icon-btn{background:none;border:1px solid transparent;border-radius:4px;font-size:16px;cursor:pointer;padding:2px 6px}.toolbar-icon-btn:hover{background:var(--hover)}.toolbar-icon-btn.active{background:#e8f0fe;border-color:#4285f4}.navi-filter-panel{border-bottom:1px solid #d8d8d8;background:#fafafa}.navi-filter-conditions{padding:10px 14px;min-height:40px}.navi-filter-row{display:flex;align-items:center;gap:6px;padding:3px 0;font-size:13px}.navi-filter-check{width:16px;height:16px;margin:0;cursor:pointer;accent-color:#4285f4}.navi-filter-col-select{color:#06c;font-weight:600;font-size:13px;border:none;background:transparent;cursor:pointer;outline:none;padding:2px 4px;appearance:none;-webkit-appearance:none}.navi-filter-col-select:hover{text-decoration:underline}.navi-filter-op{color:#333;padding:1px 2px;border:1px solid #d0d0d0;border-radius:3px;background:#fff;font-size:12px;cursor:pointer;min-width:50px;outline:none}.navi-filter-val{color:#333;cursor:pointer;padding:2px 8px;background:#fff;border:1px solid #d0d0d0;border-radius:3px;min-width:20px;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.navi-filter-val:hover{border-color:#999}.navi-filter-connector{color:#999;font-size:12px;padding:0 4px}.navi-filter-btn-icon{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;font-size:14px;font-weight:700;color:#fff;background:#4285f4;border-radius:3px;cursor:pointer;margin-left:2px}.navi-filter-btn-icon:hover{background:#3367d6}.navi-filter-btn-group{background:#7baaf7;font-size:11px}.navi-filter-btn-group:hover{background:#5a94e6}.navi-filter-remove{background:none;border:none;color:#ccc;cursor:pointer;font-size:12px;padding:0 4px;margin-left:auto}.navi-filter-remove:hover{color:#c62828}.navi-filter-group{margin:2px 0}.navi-filter-group-paren{font-size:15px;font-weight:600;color:#555;padding:0 2px}.navi-filter-group-children{padding-left:24px}.navi-filter-toolbar{display:flex;align-items:center;gap:2px;padding:4px 14px;background:#f0f0f0;border-top:1px solid #e0e0e0}.navi-filter-tool{width:26px;height:24px;display:flex;align-items:center;justify-content:center;font-size:14px;color:#555;cursor:pointer;border-radius:4px}.navi-filter-tool:hover{background:#d6d6d6}.navi-filter-apply{font-size:13px;color:#333;cursor:pointer;padding:2px 10px;border-radius:4px;font-weight:500}.navi-filter-apply:hover{background:#d6d6d6}.navi-filter-status{font-size:12px;color:#999;margin-left:8px}.navi-picker-overlay{position:absolute;inset:0;z-index:300}.navi-picker{position:absolute;top:60px;right:40px;width:380px;max-height:440px;background:#f6f6f6;border-radius:10px;box-shadow:0 8px 32px #00000040;display:flex;flex-direction:column;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.navi-picker-input{margin:12px 14px 0;padding:7px 8px;border:2px solid #4a90d9;border-radius:4px;font-size:13px;outline:none;background:#fff}.navi-picker-input:focus{border-color:#2563eb}.navi-picker-section{padding:10px 14px 4px;font-size:13px;color:#666;font-weight:500}.navi-picker-list{flex:1;overflow-y:auto;min-height:60px;max-height:220px;margin:4px 10px;background:#fff;border:1px solid #d4d4d4;border-radius:6px}.navi-picker-loading{text-align:center;padding:20px;color:#999;font-size:13px}.navi-picker-item{display:flex;align-items:center;gap:8px;padding:5px 10px;cursor:pointer;font-size:13px;border-bottom:1px solid #f0f0f0;color:#333}.navi-picker-item:last-child{border-bottom:none}.navi-picker-item:hover{background:#f0f5ff}.navi-picker-item input[type=checkbox]{width:15px;height:15px;cursor:pointer;flex-shrink:0;margin:0}.navi-picker-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.navi-picker-search-row{display:flex;align-items:center;margin:6px 14px;border:1px solid #d4d4d4;border-radius:6px;background:#fff;overflow:hidden}.navi-picker-search-icon{padding:5px 6px 5px 10px;font-size:14px;color:#999;flex-shrink:0}.navi-picker-search{flex:1;border:none;outline:none;padding:6px 8px 6px 2px;font-size:13px;background:transparent}.navi-picker-actions{display:flex;justify-content:flex-end;gap:8px;padding:8px 14px 12px}.navi-picker-btn{padding:5px 22px;border:1px solid #c0c0c0;border-radius:6px;background:#fff;font-size:13px;cursor:pointer;color:#333}.navi-picker-btn:hover{background:#eaeaea}.navi-picker-btn-ok{background:#4285f4;color:#fff;border-color:#4285f4;font-weight:500}.navi-picker-btn-ok:hover{background:#3367d6}.data-grid-wrap{flex:1;overflow:auto;min-height:0}.data-grid{width:auto;border-collapse:collapse;table-layout:auto}.query-result-grid{width:max-content;table-layout:fixed}.data-grid th{top:0;background:var(--table-header-bg);border-bottom:2px solid var(--table-border);border-right:1px solid var(--table-border);padding:4px 10px;text-align:left;font-size:var(--font-size-sm);font-weight:600;white-space:nowrap;z-index:1;min-width:60px;position:sticky;overflow:visible}.col-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:5px;cursor:col-resize;z-index:3;-webkit-user-select:none;user-select:none}.col-resize-handle:hover,.col-resize-handle:active{background:var(--accent)}.data-grid td{padding:4px 10px;border-bottom:1px solid var(--border-light);border-right:1px solid var(--border-light);font-size:var(--font-size-xs);white-space:nowrap;max-width:400px;overflow:hidden;text-overflow:ellipsis}.data-grid tr:nth-child(2n) td{background:var(--table-row-alt)}.data-grid tr:hover td{background:var(--table-row-hover)}.data-grid tr.row-selected td{background:#cce5ff!important}.data-grid tr.row-selected:hover td{background:#b8daff!important}.data-grid tr.row-selected .row-num{background:#b8daff!important;color:#004085}.data-grid td.cell-selected{outline:2px solid #4285f4;outline-offset:-2px}.data-grid tr{cursor:default}.data-grid .row-num{color:var(--text-muted);text-align:right;background:var(--table-header-bg);border-right:2px solid var(--table-border);font-family:var(--font-mono);font-size:var(--font-size-xs);min-width:40px;padding:3px 6px;position:sticky;left:0;z-index:0}.data-grid th.row-num-header{min-width:40px;z-index:2;position:sticky;left:0}.cell-null{color:var(--text-muted);font-style:italic}.data-footer{display:flex;align-items:center;justify-content:space-between;padding:4px 8px;border-top:1px solid var(--border-light);background:var(--bg-info);font-size:var(--font-size-xs);color:var(--text-secondary);flex-shrink:0;gap:8px;flex-wrap:wrap}.data-footer-right{display:flex;align-items:center;gap:12px}.data-footer-sql{font-family:var(--font-mono);color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:50%}.cell-edit-input{width:100%;min-width:60px;padding:1px 4px;border:2px solid var(--accent);border-radius:2px;font-family:var(--font-mono);font-size:var(--font-size-xs);outline:none;background:#fff;box-sizing:border-box}.cell-modified{background:#fff3cd!important}.data-grid tr.row-new td{background:#d4edda!important}.data-grid tr.row-new .row-num{background:#c3e6cb!important;color:#155724}.data-grid tr.row-deleted td{background:#f8d7da!important;text-decoration:line-through;color:#999}.data-grid tr.row-deleted .row-num{background:#f5c6cb!important;color:#721c24}.data-action-bar{display:flex;align-items:center;gap:2px;flex-shrink:0}.action-btn{width:26px;height:22px;border:1px solid var(--border);border-radius:3px;background:#fff;cursor:pointer;font-size:14px;line-height:1;display:flex;align-items:center;justify-content:center;color:var(--text-secondary);transition:background .1s,color .1s}.action-btn:hover:not(:disabled){background:var(--table-row-hover);color:var(--text)}.action-btn:disabled{opacity:.35;cursor:default}.action-btn.apply{color:#28a745;font-weight:700}.action-btn.apply:hover:not(:disabled){background:#d4edda}.action-btn.cancel{color:#dc3545;font-weight:700}.action-btn.cancel:hover:not(:disabled){background:#f8d7da}.action-status{font-size:var(--font-size-xs);color:var(--text-secondary);margin-left:8px}.pagination{display:flex;align-items:center;gap:2px}.page-btn{padding:2px 8px!important;border:1px solid var(--border)!important;border-radius:3px!important;font-size:11px!important;background:#fff!important;cursor:pointer!important;line-height:1.4!important;min-width:24px!important;text-align:center!important}.page-btn:hover{background:var(--select-bg-hover)!important}.page-btn:disabled{opacity:.35;cursor:default!important;background:#f5f5f5!important}.page-info{padding:0 6px;font-size:11px;color:var(--text-secondary);white-space:nowrap}.page-input{width:38px;padding:1px 4px;margin:0 4px;border:1px solid var(--border);border-radius:3px;font-size:11px;font-weight:600;color:var(--accent);text-align:center;background:#fff;outline:none;line-height:1.4}.page-input:focus{border-color:var(--accent);box-shadow:0 0 0 2px #2968c826}.page-count{font-size:11px;color:var(--text-secondary);white-space:nowrap;padding-left:4px;border-left:1px solid var(--border-light)}.query-pane{flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}.query-toolbar{display:flex;align-items:center;padding:2px 8px;gap:2px;border-bottom:1px solid var(--border-light);flex-shrink:0;background:var(--bg-info);height:32px}.query-toolbar select{padding:2px 6px;border:1px solid var(--border);border-radius:3px;font-size:var(--font-size-sm);background:var(--bg-main);color:var(--text)}.query-db-selector{display:flex;align-items:center;gap:4px}.query-db-selector select{padding:2px 20px 2px 6px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:var(--bg-main);min-width:140px;cursor:pointer;appearance:auto;color:var(--text);height:24px}.query-db-selector select:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px #2968c826}.tb-icon-btn{display:flex;align-items:center;justify-content:center;width:28px;height:24px;border:none;background:transparent;color:var(--text-muted, #666);border-radius:4px;cursor:pointer;padding:0;transition:all .12s ease}.tb-icon-btn:hover{background:#0000000f;color:var(--text)}.tb-icon-btn:active{background:#0000001a}.tb-icon-btn:disabled{opacity:.3;cursor:default}.tb-icon-btn:disabled:hover{background:transparent}.tb-icon-btn.tb-run{color:#2ea043}.tb-icon-btn.tb-run:hover{background:#2ea0431a;color:#238636}.toolbar-sep{width:1px;height:16px;background:var(--border);margin:0 4px;flex-shrink:0}.run-btn{padding:3px 12px!important;background:var(--accent)!important;color:#fff!important;border-radius:4px!important;font-weight:500!important;font-size:var(--font-size-sm)!important}.run-btn:hover{background:var(--accent-hover)!important}.run-btn:disabled{opacity:.5;cursor:not-allowed}.run-btn.stop{background:#d73a49!important}.run-btn.stop:hover{background:#cb2431!important}.query-split-container{flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}.query-editor-area{min-height:60px;height:200px;flex-shrink:0;position:relative;overflow:hidden}.query-editor-area .cm-editor,.query-editor-area .cm-scroller,.query-editor-area .cm-content,.query-editor-area .cm-line{user-select:text!important;-webkit-user-select:text!important}.query-split-handle{height:4px;background:var(--border-light);cursor:row-resize;flex-shrink:0;position:relative;transition:background .15s}.query-split-handle:hover,.query-split-handle:active{background:var(--accent)}.query-split-handle:after{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:30px;height:2px;border-top:1px solid rgba(0,0,0,.15);border-bottom:1px solid rgba(0,0,0,.15)}.query-results{flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden;contain:strict;isolation:isolate}.result-view-tabs{display:flex;align-items:stretch;background:var(--bg-info);border-bottom:1px solid var(--border-light);flex-shrink:0;gap:0}.result-view-actions{display:flex;align-items:center;gap:6px;padding:0 8px}.result-action-btn{min-width:38px;height:26px;padding:0 10px;border:1px solid var(--border);border-radius:6px;background:#fff;cursor:pointer;font-size:12px;line-height:1;font-weight:600;display:inline-flex;align-items:center;justify-content:center;color:var(--text-secondary);transition:background .12s,color .12s,border-color .12s,box-shadow .12s;white-space:nowrap}.result-action-btn:hover{background:var(--table-row-hover);color:var(--text);border-color:var(--accent-soft)}.result-action-btn:focus-visible{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px #2968c829}.result-action-btn-icon{min-width:30px;width:30px;padding:0;font-size:13px}.result-view-tab{padding:6px 16px;font-size:var(--font-size-xs);color:var(--text-secondary);border:1px solid transparent;border-bottom:none;border-radius:4px 4px 0 0;cursor:pointer;background:none;margin-bottom:-1px;transition:all .12s}.result-view-tab:hover{color:var(--text);background:#00000008}.result-view-tab.active{color:var(--accent);background:#fff;border-color:var(--border-light);font-weight:500}.result-grid-wrap{flex:1;display:flex;flex-direction:column;overflow:hidden;min-height:0;contain:layout paint}.virtual-grid-scroll{contain:layout paint;isolation:isolate;will-change:scroll-position}.query-messages{padding:12px 16px;font-family:var(--font-mono);font-size:var(--font-size-xs);line-height:1.7;color:var(--text);overflow:auto}.query-message-item{margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid var(--border-light)}.query-message-item:last-child{border-bottom:none;margin-bottom:0}.qm-sql{color:var(--text);word-break:break-all}.qm-info{color:#666}.qm-time{color:#999}.info-panel{width:clamp(240px,20vw,500px);background:var(--bg-info);border-left:none;display:flex;flex-direction:column;flex-shrink:0;overflow:hidden}.info-header{padding:16px 16px 12px;border-bottom:1px solid var(--border-light);display:flex;align-items:center;gap:12px;flex-shrink:0}.info-icon{width:48px;height:48px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:28px;flex-shrink:0}.info-icon.conn-icon{background:linear-gradient(135deg,#e8d5b7,#c9a96e)}.info-icon.db-icon{background:linear-gradient(135deg,#b8e6c8,#5cb87a)}.info-icon.table-icon{background:linear-gradient(135deg,#b8d4f0,#5a9fd4)}.info-title{font-size:16px;font-weight:600;line-height:1.2}.info-subtitle{font-size:var(--font-size-xs);color:var(--text-muted);margin-top:2px}.info-body{flex:1;overflow-y:auto;padding:12px 16px}.info-row{display:flex;flex-direction:column;gap:2px;margin-bottom:10px}.info-label{font-size:var(--font-size-xs);color:var(--text-muted);font-weight:500}.info-value{font-size:var(--font-size-sm);color:var(--text);word-break:break-all}.info-tab-bar{display:flex;padding:0 8px;border-bottom:1px solid var(--border-light);flex-shrink:0;background:var(--bg-info);gap:0}.info-tab{padding:6px 12px;font-size:var(--font-size-xs);color:var(--text-secondary);border:none;border-bottom:2px solid transparent;background:none;cursor:pointer;border-radius:0;font-weight:500;transition:color .12s,border-color .12s}.info-tab:hover{color:var(--text)}.info-tab.active{color:var(--accent);border-bottom-color:var(--accent)}.ddl-area{margin-top:0;padding:12px;background:#f8f8f8;border:none;border-radius:0;font-family:var(--font-mono);font-size:var(--font-size-xs);line-height:1.6;white-space:pre;overflow:auto;color:var(--text);-webkit-user-select:text;user-select:text}.sql-kw{color:#1a0dab}.sql-type{color:#0b7285}.sql-str{color:#e65100}.sql-num{color:#6a1b9a}.sql-comment{color:#6a9955;font-style:italic}.sql-punct{color:#777}.ddl-body{padding:0!important}.ddl-body .ddl-area{flex:1;max-height:none;height:100%}[data-theme=dark] .ddl-area{background:#1e1e1e}[data-theme=dark] .sql-kw{color:#569cd6}[data-theme=dark] .sql-type{color:#4ec9b0}[data-theme=dark] .sql-str{color:#ce9178}[data-theme=dark] .sql-num{color:#b5cea8}[data-theme=dark] .sql-comment{color:#6a9955}[data-theme=dark] .sql-punct{color:gray}.columns-body{padding:0!important;overflow:auto}.info-inline-actions{display:flex;justify-content:flex-end;gap:8px;padding:8px;border-bottom:1px solid var(--border-light);background:var(--bg-info)}.info-columns-table{width:100%;border-collapse:collapse;font-size:var(--font-size-xs)}.info-columns-table th{position:sticky;top:0;background:var(--table-header-bg);border-bottom:1px solid var(--table-border);padding:4px 8px;text-align:left;font-weight:600;color:var(--text-secondary);white-space:nowrap;z-index:1}.info-columns-table td{padding:3px 8px;border-bottom:1px solid var(--border-light);white-space:nowrap}.info-columns-table tr:hover td{background:var(--table-row-hover)}.info-columns-table .col-name{font-weight:500;font-family:var(--font-mono);color:var(--text)}.info-columns-table .col-type{color:var(--accent);font-family:var(--font-mono)}.info-columns-table .col-key{color:var(--warning);font-weight:600}.status-bar{height:var(--statusbar-h);background:var(--bg-statusbar);border-top:1px solid var(--border);display:flex;align-items:center;padding:0 12px;gap:16px;font-size:var(--font-size-xs);color:var(--text-secondary);flex-shrink:0;-webkit-user-select:none;user-select:none}.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block;margin-right:4px}.status-dot.online{background:var(--success)}.status-dot.offline{background:var(--text-muted)}.status-dot.connecting{width:10px;height:10px;margin-right:6px;background:transparent;border:2px solid rgba(41,104,200,.2);border-top-color:var(--accent);border-right-color:var(--accent);animation:spin-ring .78s linear infinite}.status-pill{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;border:1px solid rgba(41,104,200,.14);background:#2968c814;color:#1f5ec7}@keyframes spin-ring{to{transform:rotate(360deg)}}.modal-overlay{position:fixed;inset:0;background:var(--bg-modal-overlay);display:flex;align-items:center;justify-content:center;z-index:1000;animation:fade-in .15s ease-out}.modal-card{width:560px;max-height:80vh;background:#fff;border-radius:8px;box-shadow:0 12px 40px #00000040;display:flex;flex-direction:column;animation:slide-up .2s ease-out}.modal-title-bar{padding:14px 20px 12px;border-bottom:1px solid var(--border-light);display:flex;align-items:center;justify-content:space-between}.modal-title-bar h3{margin:0;font-size:14px;font-weight:600}.modal-close{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:4px;font-size:18px;color:var(--text-secondary)}.modal-close:hover{background:#0000000f}.modal-tabs{display:flex;padding:0 20px;border-bottom:1px solid var(--border-light)}.modal-tab{padding:8px 14px;font-size:var(--font-size-sm);color:var(--text-secondary);border-bottom:2px solid transparent}.modal-tab:hover{color:var(--text)}.modal-tab.active{color:var(--accent);border-bottom-color:var(--accent);font-weight:500}.modal-body{padding:20px;flex:1;overflow-y:auto}.modal-row{display:grid;grid-template-columns:80px 1fr;align-items:center;gap:8px;margin-bottom:12px}.modal-row label{font-size:var(--font-size-sm);color:var(--text-secondary);text-align:right}.modal-row input{padding:6px 10px;border:1px solid var(--border);border-radius:4px;font-size:var(--font-size-sm);outline:none;background:#fff}.modal-row input:focus{border-color:var(--accent)}.modal-row-split{display:grid;grid-template-columns:80px 1fr 60px 1fr;align-items:center;gap:8px;margin-bottom:12px}.modal-row-split label{font-size:var(--font-size-sm);color:var(--text-secondary);text-align:right}.modal-row-split input{padding:6px 10px;border:1px solid var(--border);border-radius:4px;font-size:var(--font-size-sm);outline:none;background:#fff}.modal-row-split input:focus{border-color:var(--accent)}.modal-diagram{display:flex;align-items:center;justify-content:center;gap:20px;padding:12px 0 20px}.modal-diagram-node{display:flex;flex-direction:column;align-items:center;gap:4px;font-size:var(--font-size-sm);color:var(--text-secondary)}.modal-diagram-icon{font-size:32px}.modal-diagram-line{width:40px;height:2px;background:var(--border)}.modal-checkbox{display:flex;align-items:center;gap:8px;margin-bottom:12px;padding-left:88px}.modal-checkbox input{width:16px;height:16px}.modal-checkbox span{font-size:var(--font-size-sm);color:var(--text-secondary)}.modal-footer{padding:12px 20px;border-top:1px solid var(--border-light);display:flex;align-items:center;justify-content:space-between;gap:8px}.modal-footer-left,.modal-footer-right{display:flex;gap:8px}.btn{padding:6px 16px;border-radius:4px;font-size:var(--font-size-sm);font-weight:500;border:1px solid var(--border);background:#fff;color:var(--text);transition:background .1s}.btn:hover{background:#f0f0f0}.btn-primary{background:var(--accent);color:#fff;border-color:var(--accent)}.btn-primary:hover{background:var(--accent-hover)}.btn-danger{color:var(--danger);border-color:#d930254d}.btn-danger:hover{background:#d930250f}.btn-sm{padding:3px 10px;font-size:var(--font-size-xs)}.btn:disabled{opacity:.5;cursor:default}.btn-link{color:var(--accent);font-size:var(--font-size-sm);padding:4px 8px}.btn-link:hover{text-decoration:underline}.profiles-section{margin-top:16px;border-top:1px solid var(--border-light);padding-top:12px}.profiles-section h4{margin:0 0 8px;font-size:var(--font-size-sm);font-weight:600;color:var(--text-secondary)}.profile-item{display:flex;align-items:center;justify-content:space-between;padding:6px 8px;border-radius:4px;cursor:pointer;font-size:var(--font-size-sm)}.profile-item:hover{background:var(--select-bg-hover)}.profile-item .profile-name{font-weight:500}.profile-item .profile-host{color:var(--text-muted);font-size:var(--font-size-xs)}.profile-actions{display:flex;gap:4px}@keyframes slide-up{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}.resize-handle{width:4px;cursor:col-resize;background:transparent;transition:background .2s;flex-shrink:0}.resize-handle:hover{background:var(--accent)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#ccc;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#aaa}.context-menu-overlay{position:fixed;inset:0;z-index:800}.context-menu{position:fixed;z-index:801;min-width:200px;background:#fffffff5;-webkit-backdrop-filter:blur(20px) saturate(180%);backdrop-filter:blur(20px) saturate(180%);border:1px solid rgba(0,0,0,.12);border-radius:8px;box-shadow:0 8px 32px #0000002e,0 2px 8px #00000014;padding:4px 0;animation:ctx-pop .12s ease-out;font-size:var(--font-size-sm);-webkit-user-select:none;user-select:none}.ctx-item{display:flex;align-items:center;gap:8px;padding:6px 16px 6px 12px;cursor:pointer;color:var(--text);border-radius:4px;margin:0 4px;white-space:nowrap}.ctx-item:hover{background:var(--select-bg);color:#fff}.ctx-item.danger{color:var(--danger)}.ctx-item.danger:hover{background:var(--danger);color:#fff}.ctx-item.disabled{color:var(--text-muted);cursor:default}.ctx-item.disabled:hover{background:none;color:var(--text-muted)}.ctx-item .ctx-icon{width:18px;text-align:center;font-size:14px;flex-shrink:0}.ctx-item .ctx-label{flex:1}.ctx-item .ctx-shortcut{color:var(--text-muted);font-size:var(--font-size-xs);margin-left:16px}.ctx-item:hover .ctx-shortcut{color:#ffffffb3}.ctx-item.has-submenu{position:relative}.ctx-submenu{display:none;position:absolute;left:100%;top:-4px;min-width:200px;background:var(--bg-info);border:1px solid var(--border);border-radius:8px;padding:4px 0;box-shadow:0 6px 24px #00000026;z-index:1100}.ctx-item.has-submenu:hover>.ctx-submenu{display:block}.ctx-sep{height:1px;background:#00000014;margin:4px 8px}@keyframes ctx-pop{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}[data-theme=dark] .context-menu{background:#2d2d2df7;border-color:#4a4a4a;box-shadow:0 8px 32px #00000080}[data-theme=dark] .ctx-sep{background:#ffffff14}[data-theme=dark] .ctx-submenu{background:#2d2d2df7;border-color:#4a4a4a;box-shadow:0 8px 32px #00000080}[data-theme=dark] .context-menu-overlay{background:transparent}[data-theme=dark] .cm-editor{background:#1e1e1e!important}[data-theme=dark] .cm-editor .cm-content{caret-color:#aeafad}[data-theme=dark] .cm-editor .cm-cursor{border-left-color:#aeafad!important}[data-theme=dark] .cm-editor .cm-activeLine{background:#2a2a2a!important}[data-theme=dark] .cm-editor .cm-gutters{background:#1e1e1e!important;border-right-color:#3c3c3c!important;color:#5a5a5a!important}[data-theme=dark] .cm-editor .cm-activeLineGutter{background:#2a2a2a!important;color:#ccc!important}[data-theme=dark] .cm-editor .cm-selectionBackground{background:#264f78!important}[data-theme=dark] .cm-editor .cm-matchingBracket{background:#3a5a3a!important;outline-color:#5a8a5a!important}[data-theme=dark] .cm-editor .cm-panels{background:#252526!important;border-bottom-color:#3c3c3c!important}[data-theme=dark] .cm-editor .cm-tooltip{background:#252526!important;border-color:#4a4a4a!important}[data-theme=dark] .cm-editor .cm-tooltip-autocomplete ul>li[aria-selected]{background:#264f78!important}[data-theme=dark] .cm-editor .cm-completionDetail{color:#6a6a6a!important}[data-theme=dark] .result-view-tab.active{background:var(--bg-main);color:#6cb6ff;border-color:var(--border-light)}[data-theme=dark] .result-view-tab:hover{background:#ffffff0d}[data-theme=dark] .qm-info{color:#8b949e}[data-theme=dark] .qm-time{color:#6e7681}[data-theme=dark] .query-split-handle{background:#30363d}[data-theme=dark] .query-split-handle:after{border-top-color:#ffffff26;border-bottom-color:#ffffff26}[data-theme=dark] .data-grid th{background:#21262d;color:#c9d1d9;border-color:#30363d}[data-theme=dark] .data-grid td{border-color:#21262d}[data-theme=dark] .data-grid tr:nth-child(2n) td{background:#ffffff05}[data-theme=dark] .data-grid tr:hover td{background:#ffffff0f}[data-theme=dark] .data-grid tr.row-selected td{background:#1c3a5c!important}[data-theme=dark] .data-grid tr.row-selected:hover td{background:#214a6e!important}[data-theme=dark] .data-grid tr.row-selected .row-num{background:#214a6e!important;color:#6cb6ff}[data-theme=dark] .data-grid .row-num{background:#161b22;color:#484f58;border-color:#21262d}[data-theme=dark] .data-grid td.cell-selected{outline-color:#58a6ff}[data-theme=dark] .col-sort-popup{background:#21262d;border-color:#30363d}[data-theme=dark] .col-sort-item:hover{background:#ffffff14}[data-theme=dark] .page-btn{color:#8b949e;border-color:#30363d}[data-theme=dark] .page-btn:hover:not(:disabled){background:#ffffff14}[data-theme=dark] .page-input{background:#0d1117;color:#c9d1d9;border-color:#30363d}[data-theme=dark] .page-count{color:#6e7681}[data-theme=dark] .summary-kv{border-color:#21262d}[data-theme=dark] .summary-kv .sk-label{color:#8b949e}[data-theme=dark] .summary-kv .sk-value{color:#c9d1d9}[data-theme=dark] .query-toolbar{background:#161b22;border-bottom-color:#30363d}[data-theme=dark] .tb-icon-btn{color:#8b949e}[data-theme=dark] .tb-icon-btn:hover{background:#ffffff14;color:#c9d1d9}[data-theme=dark] .tb-icon-btn:active{background:#ffffff1f}[data-theme=dark] .tb-icon-btn.tb-run{color:#3fb950}[data-theme=dark] .tb-icon-btn.tb-run:hover{background:#3fb95026;color:#3fb950}[data-theme=dark] .toolbar-sep{background:#30363d}[data-theme=dark] .run-btn{background:#238636!important;color:#fff!important}[data-theme=dark] .run-btn:hover{background:#2ea043!important}[data-theme=dark] .run-btn.stop{background:#da3633!important}[data-theme=dark] .run-btn.stop:hover{background:#f85149!important}[data-theme=dark] .query-toolbar select,[data-theme=dark] .query-db-selector select{background:#21262d;color:#c9d1d9;border-color:#30363d}[data-theme=dark] .query-db-selector select:focus{border-color:#58a6ff;box-shadow:0 0 0 2px #58a6ff33}[data-theme=dark] .result-view-tab.active{background:#0d1117;border-color:#30363d}[data-theme=dark] .history-panel{background:#161b22;border-color:#30363d}[data-theme=dark] .history-item{border-bottom-color:#21262d}[data-theme=dark] .history-item:hover{background:#ffffff0a}[data-theme=dark] .history-panel-header{border-bottom-color:#30363d}[data-theme=dark] .empty-state{color:#484f58}[data-theme=dark] .tab-bar{background:#161b22;border-bottom-color:#30363d}[data-theme=dark] .tab-item{color:#8b949e;border-color:transparent}[data-theme=dark] .tab-item:hover{background:#ffffff0a}[data-theme=dark] .tab-item.active{background:var(--bg-main);color:#c9d1d9;border-color:#30363d;border-bottom-color:var(--bg-main)}[data-theme=dark] .tab-close:hover{background:#ffffff1a}[data-theme=dark] .action-bar{background:#161b22;border-bottom-color:#30363d}[data-theme=dark] .action-btn{color:#8b949e}[data-theme=dark] .action-btn:hover{background:#ffffff14;color:#c9d1d9}[data-theme=dark] .pagination-bar{background:#161b22;border-top-color:#30363d}.notice-toast{position:fixed;top:calc(var(--toolbar-h) + 8px);right:12px;padding:8px 16px;border-radius:6px;font-size:var(--font-size-sm);z-index:900;animation:slide-in-right .2s ease-out;max-width:400px;box-shadow:0 4px 16px #00000026}.notice-toast.success{background:#e6f4ea;color:#137333;border:1px solid #a8dab5}.notice-toast.error{background:#fce8e6;color:#c5221f;border:1px solid #f5c6cb}@keyframes slide-in-right{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}.history-panel{position:absolute;top:100%;left:0;width:480px;max-height:360px;background:#fff;border:1px solid var(--border);border-radius:8px;box-shadow:0 8px 30px #00000026;z-index:100;display:flex;flex-direction:column;overflow:hidden;margin-top:4px}.history-panel-header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;border-bottom:1px solid var(--border-light);font-size:12px;font-weight:600}.history-panel-header button{background:none;border:none;cursor:pointer;font-size:14px;padding:2px 4px;border-radius:4px}.history-panel-header button:hover{background:#0000000f}.history-empty{padding:24px;text-align:center;font-size:12px;color:var(--text-muted)}.history-list{overflow-y:auto;flex:1}.history-item{padding:6px 12px;cursor:pointer;border-bottom:1px solid var(--border-light);transition:background 80ms}.history-item:hover{background:var(--select-bg-hover)}.history-item:last-child{border-bottom:none}.history-item code{font-family:var(--font-mono);font-size:11px;color:var(--text);white-space:pre-wrap;word-break:break-all;line-height:1.4}.cli-container{height:100%;display:flex;flex-direction:column;background:#fafbfc;cursor:text}.cli-output{flex:1;overflow-y:auto;padding:12px 16px;font-family:SF Mono,Monaco,Menlo,Consolas,monospace;font-size:13px;line-height:1.5;color:#333}.cli-welcome{color:#888;padding-bottom:8px;border-bottom:1px solid #e0e0e0;margin-bottom:8px;font-size:12px}.cli-entry{margin-bottom:6px}.cli-prompt-line{display:flex;align-items:baseline}.cli-prompt{color:#2e7d32;font-weight:600;flex-shrink:0;-webkit-user-select:none;user-select:none}.cli-cmd{color:#1565c0}.cli-result{margin:2px 0 0;white-space:pre;font-family:inherit;font-size:12px;color:#444;overflow-x:auto}.cli-result.cli-error{color:#c62828}.cli-input-line{display:flex;align-items:center;padding-top:2px}.cli-input{flex:1;background:transparent;border:none;outline:none;color:#1565c0;font-family:inherit;font-size:13px;caret-color:#333}.cli-input::placeholder{color:#aaa}.cli-input:disabled{opacity:.5}.import-wizard-overlay{position:fixed;inset:0;background:var(--bg-modal-overlay);z-index:1000;display:flex;align-items:center;justify-content:center;animation:fade-in .15s ease-out}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.import-wizard-modal{background:var(--bg-main);border-radius:10px;box-shadow:0 20px 60px #0000004d;width:760px;max-width:90vw;max-height:85vh;display:flex;flex-direction:column;overflow:hidden;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:translateY(20px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}.import-wizard-header{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--border-light);flex-shrink:0}.import-wizard-title{font-size:15px;font-weight:600}.import-close-btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:6px;font-size:14px;color:var(--text-secondary)}.import-close-btn:hover{background:#00000014}.import-step-indicator{display:flex;align-items:center;justify-content:center;gap:4px;padding:14px 20px 10px;flex-shrink:0}.import-step-dot{display:flex;flex-direction:column;align-items:center;gap:4px;flex:1;position:relative}.import-step-dot:after{content:"";position:absolute;top:12px;left:calc(50% + 14px);right:calc(-50% + 14px);height:2px;background:var(--border)}.import-step-dot:last-child:after{display:none}.import-step-dot.done:after{background:var(--accent)}.import-step-dot-circle{width:24px;height:24px;border-radius:50%;background:var(--border-light);color:var(--text-secondary);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:600;z-index:1;transition:all .2s}.import-step-dot.active .import-step-dot-circle{background:var(--accent);color:#fff;box-shadow:0 0 0 3px #2968c833}.import-step-dot.done .import-step-dot-circle{background:var(--success);color:#fff}.import-step-dot-label{font-size:11px;color:var(--text-muted);white-space:nowrap}.import-step-dot.active .import-step-dot-label{color:var(--accent);font-weight:500}.import-step-dot.done .import-step-dot-label{color:var(--success)}.import-wizard-body{flex:1;overflow-y:auto;padding:16px 24px;min-height:300px}.import-step-title{font-size:15px;font-weight:600;margin-bottom:4px}.import-step-desc{font-size:12px;color:var(--text-secondary);margin-bottom:14px}.import-dropzone{border:2px dashed var(--border);border-radius:10px;padding:32px 20px;text-align:center;cursor:pointer;transition:border-color .2s,background .2s;margin-bottom:14px}.import-dropzone:hover{border-color:var(--accent);background:#2968c80a}.import-dropzone-hint{display:flex;flex-direction:column;align-items:center;gap:6px;color:var(--text-secondary);font-size:13px}.import-file-info{display:flex;align-items:center;justify-content:center;gap:8px;font-size:13px}.import-file-icon{font-size:24px}.import-file-name{font-weight:500}.import-file-size{color:var(--text-muted);font-size:12px}.import-options-row{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-secondary);flex-wrap:wrap}.import-options-row select{padding:3px 6px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:#fff}.import-options-row input[type=checkbox]{margin:0;accent-color:var(--accent)}.import-preview-table-wrap{max-height:340px;overflow:auto;border:1px solid var(--border-light);border-radius:6px}.import-preview-table{width:100%;border-collapse:collapse;font-size:12px}.import-preview-table th{position:sticky;top:0;background:var(--table-header-bg);padding:5px 10px;text-align:left;font-weight:500;color:var(--text-secondary);border-bottom:1px solid var(--table-border);white-space:nowrap;z-index:1}.import-preview-table td{padding:3px 10px;border-bottom:1px solid var(--border-light);white-space:nowrap;max-width:200px;overflow:hidden;text-overflow:ellipsis}.import-preview-table .import-row-num{color:var(--text-muted);text-align:center;width:36px;font-size:11px}.import-preview-table .cell-null{color:var(--text-muted);font-style:italic}.import-preview-hint{font-size:11px;color:var(--text-muted);text-align:center;padding:6px 0}.import-target-form{display:flex;flex-direction:column;gap:12px}.import-form-row{display:flex;align-items:center;gap:8px}.import-form-row label{width:80px;font-size:13px;font-weight:500;flex-shrink:0;text-align:right}.import-form-row select{flex:1;padding:6px 10px;border:1px solid var(--border);border-radius:5px;font-size:13px;background:#fff}.import-loading{font-size:12px;color:var(--accent)}.import-mapping-table-wrap{max-height:360px;overflow:auto;border:1px solid var(--border-light);border-radius:6px}.import-mapping-table{width:100%;border-collapse:collapse;font-size:12px}.import-mapping-table th{position:sticky;top:0;background:var(--table-header-bg);padding:6px 10px;text-align:left;font-weight:500;color:var(--text-secondary);border-bottom:1px solid var(--table-border);z-index:1}.import-mapping-table td{padding:4px 10px;border-bottom:1px solid var(--border-light)}.import-mapping-table code{background:#0000000a;padding:1px 5px;border-radius:3px;font-family:var(--font-mono);font-size:12px}.import-mapping-table select{width:100%;padding:4px 6px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:#fff}.import-mapping-skipped{opacity:.4}.import-preview-cell{max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-secondary);font-family:var(--font-mono);font-size:11px}.import-mapping-summary{font-size:12px;color:var(--text-secondary);text-align:right;padding:8px 0 0}.import-stats-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px}.import-stat{background:var(--bg-info);border:1px solid var(--border-light);border-radius:8px;padding:12px;text-align:center}.import-stat-label{font-size:11px;color:var(--text-secondary);margin-bottom:4px}.import-stat-value{font-size:20px;font-weight:600}.import-progress-bar-wrap{height:8px;background:var(--border-light);border-radius:4px;overflow:hidden;margin-bottom:6px}.import-progress-bar{height:100%;background:linear-gradient(90deg,var(--accent),#4fc3f7);border-radius:4px;transition:width .3s ease}.import-progress-text{font-size:12px;color:var(--text-secondary);text-align:center}.import-error-log{margin-top:14px}.import-error-list{max-height:120px;overflow-y:auto;border:1px solid #f5c6cb;border-radius:6px;background:#fef5f5;padding:8px 12px}.import-error-item{font-size:11px;color:#c62828;padding:2px 0;font-family:var(--font-mono)}.import-wizard-footer{display:flex;align-items:center;padding:12px 20px;border-top:1px solid var(--border-light);gap:8px;flex-shrink:0}.import-btn{padding:7px 18px;border-radius:6px;font-size:13px;font-weight:500;border:none;cursor:pointer;transition:background .12s}.import-btn:disabled{opacity:.4;cursor:not-allowed}.import-btn-primary{background:var(--accent);color:#fff}.import-btn-primary:hover:not(:disabled){background:var(--accent-hover)}.import-btn-secondary{background:var(--border-light);color:var(--text)}.import-btn-secondary:hover:not(:disabled){background:var(--border)}.combo-box-input{width:100%;padding:6px 10px;border:1px solid var(--border);border-radius:5px;font-size:13px;background:#fff;outline:none;transition:border-color .15s}.combo-box-input:focus{border-color:var(--accent)}.combo-box-dropdown{position:absolute;top:100%;left:0;right:0;z-index:100;background:#fff;border:1px solid var(--border);border-top:none;border-radius:0 0 6px 6px;box-shadow:0 6px 20px #00000026;max-height:240px;overflow-y:auto}.combo-box-item{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;font-size:12px;cursor:pointer;transition:background 80ms}.combo-box-item:hover{background:var(--select-bg-hover)}.combo-box-item.selected{background:var(--select-bg);color:var(--select-text)}.combo-box-item.combo-box-new{color:var(--accent);border-bottom:1px solid var(--border-light)}.combo-box-meta{font-size:11px;color:var(--text-muted)}.combo-box-item.selected .combo-box-meta{color:#ffffffb3}.combo-box-empty{padding:8px 10px;font-size:12px;color:var(--text-muted);text-align:center}.export-step-title{font-size:15px;font-weight:600;margin-bottom:4px}.export-step-desc{font-size:12px;color:var(--text-secondary);margin-bottom:14px}.export-format-list{display:flex;flex-direction:column;gap:6px}.export-format-item{display:flex;align-items:center;gap:10px;padding:10px 14px;border:1px solid var(--border-light);border-radius:8px;cursor:pointer;transition:border-color .15s,background .15s}.export-format-item:hover{border-color:var(--border);background:#00000004}.export-format-item.selected{border-color:var(--accent);background:#2968c80a}.export-format-item input[type=radio]{accent-color:var(--accent);margin:0}.export-format-info{display:flex;flex-direction:column;gap:2px}.export-format-label{font-size:13px;font-weight:500}.export-format-desc{font-size:11px;color:var(--text-muted)}.export-form-row{display:flex;align-items:center;gap:8px;margin-bottom:12px}.export-form-row label{width:70px;font-size:13px;font-weight:500;flex-shrink:0;text-align:right}.export-form-row select{flex:1;padding:6px 10px;border:1px solid var(--border);border-radius:5px;font-size:13px;background:#fff}.export-table-search-row{display:flex;align-items:center;gap:4px;margin-bottom:6px}.export-table-search{flex:1;padding:5px 10px;border:1px solid var(--border);border-radius:5px;font-size:12px;background:#fff;outline:none}.export-table-search:focus{border-color:var(--accent)}.export-table-search-clear{padding:2px 6px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:#fff;cursor:pointer;color:var(--text-muted)}.export-table-search-clear:hover{color:var(--danger);border-color:var(--danger)}.export-table-list-header{font-size:12px;padding:4px 0;color:var(--text-secondary);display:flex;align-items:center;gap:6px}.export-table-list-header input[type=checkbox]{accent-color:var(--accent)}.export-table-list{border:1px solid var(--border-light);border-radius:6px;max-height:280px;overflow-y:auto}.export-table-list-row{display:flex;align-items:center;gap:8px;padding:5px 10px;font-size:12px;border-bottom:1px solid var(--border-light)}.export-table-list-row:last-child{border-bottom:none}.export-table-list-head{background:var(--table-header-bg);font-weight:500;color:var(--text-secondary);position:sticky;top:0;z-index:1}.export-table-list-row.disabled{opacity:.4}.export-table-list-row input[type=checkbox]{accent-color:var(--accent);margin:0}.export-table-name{display:flex;align-items:center;gap:4px}.export-table-icon{font-size:14px}.export-filename-input{width:100%;padding:2px 6px;border:1px solid var(--border-light);border-radius:3px;font-size:12px;font-family:var(--font-mono);background:#fff}.export-filename-input:focus{border-color:var(--accent);outline:none}.export-loading{font-size:12px;color:var(--accent);padding:20px;text-align:center}.export-col-list{border:1px solid var(--border-light);border-radius:6px;max-height:300px;overflow-y:auto;padding:4px 0}.export-col-item{display:flex;align-items:center;gap:8px;padding:4px 14px;cursor:pointer;font-size:13px;transition:background .1s}.export-col-item:hover{background:var(--select-bg-hover)}.export-col-item input[type=checkbox]{accent-color:var(--accent);margin:0}.export-col-name{flex:1}.export-col-type{font-size:11px;color:var(--text-muted);font-family:var(--font-mono)}.export-col-key{font-size:12px}.export-col-actions{display:flex;align-items:center;gap:8px;padding:8px 0 0;font-size:12px}.export-col-actions button{padding:3px 10px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:#fff;cursor:pointer}.export-col-actions button:hover{background:var(--select-bg-hover)}.export-col-actions input[type=checkbox]{accent-color:var(--accent)}.export-options-section{margin-bottom:16px}.export-options-title{font-size:13px;font-weight:600;margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid var(--border-light)}.export-options-grid{display:flex;flex-direction:column;gap:8px}.export-options-grid input[type=checkbox]{accent-color:var(--accent)}.export-option-row{display:flex;align-items:center;gap:8px;font-size:13px}.export-option-row span:first-child{width:110px;text-align:right;color:var(--text-secondary);flex-shrink:0}.export-option-row select{padding:3px 8px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:#fff}.export-stats-row{display:flex;gap:20px;margin-bottom:12px;font-size:13px}.export-stat-item{display:flex;gap:6px}.export-stat-label{font-weight:500;color:var(--text-secondary)}.export-log-box{border:1px solid var(--border-light);border-radius:6px;background:var(--bg-info);min-height:180px;max-height:280px;overflow-y:auto;padding:8px 12px;font-family:var(--font-mono);font-size:12px;margin-bottom:12px}.export-log-line{padding:2px 0;line-height:1.5}.td-container{display:flex;flex-direction:column;height:100%;background:var(--bg-main)}.td-topbar{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;border-bottom:1px solid var(--border);background:var(--bg-info);gap:12px;flex-shrink:0}.td-topbar-left{display:flex;align-items:center;gap:8px}.td-name-label{font-size:12px;color:var(--text-secondary);font-weight:500}.td-name-input{width:220px;padding:4px 8px;border:1px solid var(--border);border-radius:4px;font-size:13px;font-family:var(--font-mono);background:var(--bg-main);color:var(--text)}.td-topbar-right{display:flex;gap:6px}.td-btn{padding:4px 10px;border:1px solid var(--border);border-radius:4px;background:var(--bg-info);color:var(--text);font-size:12px;cursor:pointer;white-space:nowrap}.td-btn:hover{background:var(--select-bg-hover)}.td-btn:disabled{opacity:.4;cursor:default}.td-btn-primary{background:var(--accent);color:#fff;border-color:var(--accent)}.td-btn-primary:hover{background:var(--accent-hover)}.td-tab-bar{display:flex;border-bottom:1px solid var(--border);background:var(--bg-info);flex-shrink:0}.td-tab{padding:6px 16px;font-size:12px;font-weight:500;color:var(--text-secondary);background:none;border:none;border-bottom:2px solid transparent;cursor:pointer}.td-tab:hover{color:var(--text)}.td-tab.active{color:var(--accent);border-bottom-color:var(--accent)}.td-field-toolbar{display:flex;align-items:center;gap:4px;padding:6px 12px;border-bottom:1px solid var(--border-light);background:var(--bg-info);flex-shrink:0}.td-toolbar-sep{width:1px;height:18px;background:var(--border);margin:0 4px}.td-grid-wrapper{flex:1;overflow:auto}.td-grid{width:100%;border-collapse:collapse;font-size:12px}.td-grid thead th{position:sticky;top:0;background:var(--table-header-bg);border-bottom:1px solid var(--border);padding:5px 6px;text-align:left;font-weight:600;color:var(--text-secondary);font-size:11px;white-space:nowrap;z-index:1}.td-grid tbody tr{border-bottom:1px solid var(--border-light);cursor:pointer}.td-grid tbody tr:hover{background:var(--table-row-hover)}.td-grid tbody tr.td-row-selected{background:var(--select-bg-hover)}.td-grid td{padding:1px 2px}.td-cell-num{text-align:center;color:var(--text-muted);font-size:10px;width:30px}.td-cell-input{width:100%;padding:3px 6px;border:1px solid transparent;border-radius:3px;font-size:12px;font-family:var(--font-mono);background:transparent;color:var(--text);outline:none}.td-cell-input:focus{border-color:var(--accent);background:var(--bg-main)}.td-cell-select{width:100%;padding:3px 4px;border:1px solid transparent;border-radius:3px;font-size:12px;font-family:var(--font-mono);background:transparent;color:var(--text);outline:none;cursor:pointer}.td-cell-select:focus{border-color:var(--accent);background:var(--bg-main)}.td-cell-center{text-align:center}.td-cell-short{width:100%}.td-sql-preview{flex:1;overflow:auto;padding:12px;background:var(--bg-main)}.td-sql-preview pre{margin:0;font-family:var(--font-mono);font-size:12px;line-height:1.6;color:var(--text);white-space:pre-wrap;word-break:break-all}

Sorry, the diff of this file is too big to display

import crypto from 'node:crypto'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import type { Client, ClientChannel, ConnectConfig } from 'ssh2'
import type { ConnectionConfig } from './mysql.js'
type RawSshTarget = {
host?: unknown
port?: unknown
user?: unknown
}
export type SshExecRequest = {
command?: unknown
timeoutMs?: unknown
sshTarget?: RawSshTarget
}
export type SshExecResult = {
host: string
port: number
user: string
command: string
stdout: string
stderr: string
exitCode: number | null
signal?: string
durationMs: number
truncated: boolean
}
export type SshShellSessionStartRequest = {
sshTarget?: RawSshTarget
cols?: unknown
rows?: unknown
term?: unknown
idleTimeoutMs?: unknown
}
export type SshShellSessionReadRequest = {
sessionId?: unknown
afterSeq?: unknown
limit?: unknown
}
export type SshShellSessionInputRequest = {
sessionId?: unknown
input?: unknown
appendNewline?: unknown
}
export type SshShellSessionResizeRequest = {
sessionId?: unknown
cols?: unknown
rows?: unknown
}
export type SshShellSessionCloseRequest = {
sessionId?: unknown
}
type ShellChunkStream = 'stdout' | 'stderr' | 'system'
export type SshShellChunk = {
seq: number
stream: ShellChunkStream
text: string
timestamp: number
}
export type SshShellSessionStartResult = {
sessionId: string
host: string
port: number
user: string
nextSeq: number
createdAt: number
}
export type SshShellSessionReadResult = {
sessionId: string
host: string
port: number
user: string
chunks: SshShellChunk[]
nextSeq: number
closed: boolean
exitCode: number | null
signal?: string
lastActiveAt: number
}
type ShellSessionRecord = {
id: string
host: string
port: number
user: string
client: Client
stream: ClientChannel
createdAt: number
lastActiveAt: number
idleTimeoutMs: number
nextSeq: number
chunks: SshShellChunk[]
closed: boolean
exitCode: number | null
signal?: string
}
const DEFAULT_TIMEOUT_MS = 15_000
const MAX_TIMEOUT_MS = 60_000
const MAX_OUTPUT_BYTES = 64 * 1024
const DEFAULT_SHELL_IDLE_TIMEOUT_MS = 15 * 60 * 1000
const MAX_SHELL_IDLE_TIMEOUT_MS = 60 * 60 * 1000
const DEFAULT_SHELL_COLS = 160
const DEFAULT_SHELL_ROWS = 36
const MAX_SHELL_SESSIONS = 24
const MAX_SHELL_CHUNKS = 2_400
const MAX_SHELL_CHUNK_BYTES = 8 * 1024
const shellSessions = new Map<string, ShellSessionRecord>()
let shellGcTimer: NodeJS.Timeout | undefined
function clamp(value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value))
}
function parsePositiveInt(value: unknown, fallback: number) {
const parsed = Number.parseInt(String(value ?? ''), 10)
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback
}
function parseNonNegativeInt(value: unknown, fallback: number) {
const parsed = Number.parseInt(String(value ?? ''), 10)
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback
}
function readBoolean(value: unknown, fallback: boolean) {
if (typeof value === 'boolean') {
return value
}
if (typeof value === 'string') {
const normalized = value.trim().toLowerCase()
if (['true', '1', 'yes', 'on'].includes(normalized)) {
return true
}
if (['false', '0', 'no', 'off', ''].includes(normalized)) {
return false
}
}
return fallback
}
function normalizeTarget(target: RawSshTarget | undefined) {
return {
host: target?.host ? String(target.host).trim() : '',
port: target?.port ? parsePositiveInt(target.port, 22) : undefined,
user: target?.user ? String(target.user).trim() : '',
}
}
function resolveHomePath(filePath: string) {
if (!filePath.startsWith('~')) {
return filePath
}
return path.join(os.homedir(), filePath.slice(1))
}
function buildSshConfig(connection: ConnectionConfig, target: ReturnType<typeof normalizeTarget>) {
const host = target.host || connection.sshHost || ''
const port = target.port ?? connection.sshPort ?? 22
const user = target.user || connection.sshUser || 'root'
if (!host) {
throw new Error('缺少 SSH 主机。请先在连接里配置 SSH,或在工作台里填写目标主机。')
}
const sshConfig: ConnectConfig = {
host,
port,
username: user,
keepaliveInterval: 15_000,
keepaliveCountMax: 3,
readyTimeout: 15_000,
}
if (connection.sshAuthMethod === 'password') {
sshConfig.password = connection.sshPassword ?? ''
} else {
const keyPath = resolveHomePath(connection.sshPrivateKey ?? '~/.ssh/id_rsa')
try {
sshConfig.privateKey = fs.readFileSync(keyPath)
} catch (error) {
throw new Error(
`无法读取 SSH 私钥: ${keyPath}${error instanceof Error ? ` (${error.message})` : ''}`,
)
}
if (connection.sshPassphrase) {
sshConfig.passphrase = connection.sshPassphrase
}
}
return { host, port, user, sshConfig }
}
function appendLimited(current: string, usedBytes: number, incoming: Buffer | string) {
const chunk = Buffer.isBuffer(incoming) ? incoming : Buffer.from(incoming)
if (usedBytes >= MAX_OUTPUT_BYTES) {
return { text: current, usedBytes, truncated: true }
}
const remaining = MAX_OUTPUT_BYTES - usedBytes
if (chunk.length <= remaining) {
return {
text: current + chunk.toString('utf8'),
usedBytes: usedBytes + chunk.length,
truncated: false,
}
}
return {
text: current + chunk.subarray(0, remaining).toString('utf8'),
usedBytes: usedBytes + remaining,
truncated: true,
}
}
function normalizeChunkText(raw: Buffer | string) {
const chunk = Buffer.isBuffer(raw) ? raw : Buffer.from(raw)
if (chunk.length <= MAX_SHELL_CHUNK_BYTES) {
return chunk.toString('utf8')
}
return `${chunk.subarray(0, MAX_SHELL_CHUNK_BYTES).toString('utf8')}\n...[chunk truncated]`
}
function touchSession(session: ShellSessionRecord) {
session.lastActiveAt = Date.now()
}
function pushShellChunk(session: ShellSessionRecord, stream: ShellChunkStream, text: string) {
if (!text) {
return
}
session.nextSeq += 1
session.chunks.push({
seq: session.nextSeq,
stream,
text,
timestamp: Date.now(),
})
if (session.chunks.length > MAX_SHELL_CHUNKS) {
session.chunks.splice(0, session.chunks.length - MAX_SHELL_CHUNKS)
}
}
function ensureShellGcTimer() {
if (shellGcTimer) {
return
}
shellGcTimer = setInterval(() => {
const now = Date.now()
for (const [sessionId, session] of shellSessions.entries()) {
if (!session.closed && now - session.lastActiveAt > session.idleTimeoutMs) {
closeShellSessionInternal(session, 'Session idle timeout reached.')
}
if (session.closed && now - session.lastActiveAt > 60_000) {
shellSessions.delete(sessionId)
}
}
if (shellSessions.size === 0 && shellGcTimer) {
clearInterval(shellGcTimer)
shellGcTimer = undefined
}
}, 30_000)
}
function getSessionId(value: unknown) {
const sessionId = String(value ?? '').trim()
if (!sessionId) {
throw new Error('缺少 sessionId。')
}
return sessionId
}
function getShellSession(sessionId: string) {
const session = shellSessions.get(sessionId)
if (!session) {
throw new Error('SSH 会话不存在或已过期。')
}
return session
}
function closeShellSessionInternal(session: ShellSessionRecord, reason?: string) {
if (session.closed) {
return
}
session.closed = true
touchSession(session)
if (reason) {
pushShellChunk(session, 'system', reason)
}
try {
session.stream.end('exit\n')
} catch {
// noop
}
try {
session.stream.close()
} catch {
// noop
}
try {
session.client.end()
} catch {
// noop
}
}
export async function executeSshCommand(
connection: ConnectionConfig,
request: SshExecRequest,
): Promise<SshExecResult> {
const command = String(request.command ?? '').trim()
if (!command) {
throw new Error('SSH 命令不能为空。')
}
const timeoutMs = Math.min(parsePositiveInt(request.timeoutMs, DEFAULT_TIMEOUT_MS), MAX_TIMEOUT_MS)
const target = normalizeTarget(request.sshTarget)
const { host, port, user, sshConfig } = buildSshConfig(connection, target)
const { Client } = await import('ssh2')
return new Promise((resolve, reject) => {
const client = new Client()
const startedAt = Date.now()
let settled = false
let stdout = ''
let stderr = ''
let stdoutBytes = 0
let stderrBytes = 0
let truncated = false
let exitCode: number | null = null
let signal: string | undefined
let activeStream:
| {
close?: () => void
}
| undefined
const timeout = setTimeout(() => {
if (settled) {
return
}
settled = true
activeStream?.close?.()
client.end()
reject(new Error(`SSH 命令执行超时(>${timeoutMs}ms)`))
}, timeoutMs)
const cleanup = () => {
clearTimeout(timeout)
client.removeAllListeners()
client.end()
}
const rejectOnce = (error: Error) => {
if (settled) {
return
}
settled = true
cleanup()
reject(error)
}
const resolveOnce = () => {
if (settled) {
return
}
settled = true
cleanup()
resolve({
host,
port,
user,
command,
stdout,
stderr,
exitCode,
signal,
durationMs: Date.now() - startedAt,
truncated,
})
}
client
.on('ready', () => {
client.exec(command, (error, stream) => {
if (error) {
rejectOnce(error)
return
}
activeStream = stream
stream.on('close', (code: number | null, streamSignal: string | undefined) => {
exitCode = typeof code === 'number' ? code : null
signal = streamSignal
resolveOnce()
})
stream.on('data', (chunk: Buffer | string) => {
const next = appendLimited(stdout, stdoutBytes, chunk)
stdout = next.text
stdoutBytes = next.usedBytes
truncated ||= next.truncated
})
stream.stderr.on('data', (chunk: Buffer | string) => {
const next = appendLimited(stderr, stderrBytes, chunk)
stderr = next.text
stderrBytes = next.usedBytes
truncated ||= next.truncated
})
stream.on('error', (streamError: Error) => {
rejectOnce(streamError)
})
})
})
.on('error', (error: Error) => {
rejectOnce(error)
})
client.connect(sshConfig)
})
}
export async function startSshShellSession(
connection: ConnectionConfig,
request: SshShellSessionStartRequest,
): Promise<SshShellSessionStartResult> {
if (shellSessions.size >= MAX_SHELL_SESSIONS) {
throw new Error('当前 SSH 会话过多,请先关闭一些会话。')
}
const target = normalizeTarget(request.sshTarget)
const { host, port, user, sshConfig } = buildSshConfig(connection, target)
const cols = clamp(parsePositiveInt(request.cols, DEFAULT_SHELL_COLS), 40, 320)
const rows = clamp(parsePositiveInt(request.rows, DEFAULT_SHELL_ROWS), 12, 120)
const term = String(request.term ?? 'xterm-256color').trim() || 'xterm-256color'
const idleTimeoutMs = Math.min(
parsePositiveInt(request.idleTimeoutMs, DEFAULT_SHELL_IDLE_TIMEOUT_MS),
MAX_SHELL_IDLE_TIMEOUT_MS,
)
const { Client } = await import('ssh2')
return new Promise((resolve, reject) => {
const client = new Client()
let settled = false
const startedAt = Date.now()
const timeout = setTimeout(() => {
if (settled) {
return
}
settled = true
client.end()
reject(new Error('SSH 会话建立超时。'))
}, 20_000)
const rejectOnce = (error: Error) => {
if (settled) {
return
}
settled = true
clearTimeout(timeout)
client.end()
reject(error)
}
client
.on('ready', () => {
client.shell({ term, cols, rows }, (error, stream) => {
if (error) {
rejectOnce(error)
return
}
const sessionId = crypto.randomUUID()
const session: ShellSessionRecord = {
id: sessionId,
host,
port,
user,
client,
stream,
createdAt: Date.now(),
lastActiveAt: Date.now(),
idleTimeoutMs,
nextSeq: 0,
chunks: [],
closed: false,
exitCode: null,
}
pushShellChunk(
session,
'system',
`Connected to ${user}@${host}:${port} in ${Date.now() - startedAt}ms`,
)
stream.on('data', (chunk: Buffer | string) => {
touchSession(session)
pushShellChunk(session, 'stdout', normalizeChunkText(chunk))
})
stream.stderr.on('data', (chunk: Buffer | string) => {
touchSession(session)
pushShellChunk(session, 'stderr', normalizeChunkText(chunk))
})
stream.on('close', (code: number | undefined, signal: string | undefined) => {
session.exitCode = typeof code === 'number' ? code : null
session.signal = signal
closeShellSessionInternal(
session,
`Session closed${session.exitCode != null ? ` (exit=${session.exitCode})` : ''}${signal ? ` signal=${signal}` : ''}.`,
)
})
stream.on('error', (streamError: Error) => {
closeShellSessionInternal(session, `Stream error: ${streamError.message}`)
})
client.on('error', (clientError: Error) => {
closeShellSessionInternal(session, `SSH error: ${clientError.message}`)
})
client.on('close', () => {
closeShellSessionInternal(session, 'SSH connection closed.')
})
shellSessions.set(sessionId, session)
ensureShellGcTimer()
settled = true
clearTimeout(timeout)
resolve({
sessionId,
host,
port,
user,
nextSeq: session.nextSeq,
createdAt: session.createdAt,
})
})
})
.on('error', (error: Error) => {
rejectOnce(error)
})
client.connect(sshConfig)
})
}
export function readSshShellSession(request: SshShellSessionReadRequest): SshShellSessionReadResult {
const sessionId = getSessionId(request.sessionId)
const afterSeq = parseNonNegativeInt(request.afterSeq, 0)
const limit = clamp(parsePositiveInt(request.limit, 200), 1, 500)
const session = getShellSession(sessionId)
touchSession(session)
const chunks = session.chunks.filter((chunk) => chunk.seq > afterSeq).slice(0, limit)
return {
sessionId: session.id,
host: session.host,
port: session.port,
user: session.user,
chunks,
nextSeq: session.nextSeq,
closed: session.closed,
exitCode: session.exitCode,
signal: session.signal,
lastActiveAt: session.lastActiveAt,
}
}
export function writeSshShellInput(request: SshShellSessionInputRequest) {
const sessionId = getSessionId(request.sessionId)
const session = getShellSession(sessionId)
if (session.closed) {
throw new Error('SSH 会话已关闭。')
}
const input = String(request.input ?? '')
if (!input.trim()) {
throw new Error('输入不能为空。')
}
if (Buffer.byteLength(input, 'utf8') > 8 * 1024) {
throw new Error('输入过长,单条命令请控制在 8KB 以内。')
}
const appendNewline = readBoolean(request.appendNewline, true)
const payload = appendNewline ? `${input}\n` : input
touchSession(session)
session.stream.write(payload)
return {
ok: true as const,
sessionId: session.id,
nextSeq: session.nextSeq,
}
}
export function resizeSshShellSession(request: SshShellSessionResizeRequest) {
const sessionId = getSessionId(request.sessionId)
const session = getShellSession(sessionId)
if (session.closed) {
throw new Error('SSH 会话已关闭。')
}
const cols = clamp(parsePositiveInt(request.cols, DEFAULT_SHELL_COLS), 40, 320)
const rows = clamp(parsePositiveInt(request.rows, DEFAULT_SHELL_ROWS), 12, 120)
if (typeof session.stream.setWindow === 'function') {
session.stream.setWindow(rows, cols, 0, 0)
}
touchSession(session)
return {
ok: true as const,
sessionId: session.id,
}
}
export function closeSshShellSession(request: SshShellSessionCloseRequest) {
const sessionId = getSessionId(request.sessionId)
const session = shellSessions.get(sessionId)
if (!session) {
return {
ok: true as const,
}
}
closeShellSessionInternal(session, 'Session closed by client.')
shellSessions.delete(sessionId)
return {
ok: true as const,
}
}
export function closeAllSshShellSessions() {
for (const session of shellSessions.values()) {
closeShellSessionInternal(session, 'Server shutdown.')
}
shellSessions.clear()
if (shellGcTimer) {
clearInterval(shellGcTimer)
shellGcTimer = undefined
}
}
+2
-2

@@ -8,4 +8,4 @@ <!doctype html>

<title>NaviDog</title>
<script type="module" crossorigin src="/assets/index-0PzmurUZ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-8MrvfZ4H.css">
<script type="module" crossorigin src="/assets/index-gmMCNTS3.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DAqpsxv-.css">
</head>

@@ -12,0 +12,0 @@ <body>

{
"name": "navidog",
"version": "1.2.2",
"version": "1.2.5",
"description": "🐕 轻量级 Web 端 MySQL 管理工具,风格类似 Navicat",

@@ -5,0 +5,0 @@ "repository": {

+23
-15
# 🐕 NaviDog
一个轻量级的 Web 端 MySQL 管理工具,风格类似 Navicat。
一个轻量级的 Web 端 MySQL 管理工具,风格类似 Navicat。开箱即用,本地启动,适合日常查库、改表、导入导出和临时数据处理。
## 界面预览
![NaviDog 界面预览](./docs/images/navidog-overview.png)
## 功能
- 🔗 **连接管理** — 支持 SSH 隧道(密码/密钥)
- 📥 **NCX 导入/导出** — 浏览器端直接解析 Navicat 连接文件(含密码解密)
- 📊 **数据浏览** — 表格数据查看、筛选、排序、分页
- 📝 **SQL 编辑器** — 语法高亮、自动补全、多结果集(消息/摘要视图)
- 📋 **表管理** — 复制表(结构/数据)、删除表、清空表
- 💾 **数据导出** — 转储单表/整库为 SQL 文件
- 📄 **DDL 查看** — SQL 语法高亮显示
- 🖥️ **面板控制** — 左/右面板可折叠、可拖拽调整大小
- 🔗 **连接管理** — 保存多个 MySQL 连接,支持 SSH 隧道、连接测试、快速重连
- 📥 **NCX 导入/导出** — 浏览器端直接解析 Navicat 连接文件,支持密码解密与重新导出
- 📝 **SQL 编辑器** — 语法高亮、Schema 自动补全、SQL 格式化、运行当前语句或选中片段、多结果集展示、运行中止
- 📊 **数据浏览与编辑** — 分页、筛选、排序、列宽拖拽、行选择、复制为 INSERT / UPDATE / TSV,支持表格内增删改后批量提交
- 🧱 **表结构设计** — 可视化新建表 / 修改表,实时 SQL 预览,查看 DDL、列、索引、外键信息
- 🔑 **索引与外键管理** — 在对象信息面板中直接新增 / 删除索引和外键
- 📥 **导入向导** — 支持 CSV、TXT、Excel、JSON、SQL,提供数据预览、字段映射、自动建表,并支持 `append` / `replace` / `upsert`
- 📤 **导出向导** — 支持 CSV、TXT、XLSX、JSON、SQL,自定义表、列和导出选项,适合大表分批导出
- 📋 **表级操作** — 复制表(仅结构 / 结构+数据)、删除表、清空表、查看对象列表、运行 SQL 文件
- 💾 **转储能力** — 支持单表或整库导出结构 / 结构+数据为 SQL 文件
- 🖥️ **布局体验** — 左右面板可折叠、可拖拽调整大小,适合宽屏和小屏切换

@@ -79,8 +86,8 @@ ## 快速开始

1. 打开浏览器访问 `http://127.0.0.1:3001`
2. 点击工具栏「连接」创建 MySQL 连接
3. 填写主机、端口、用户名、密码(支持 SSH 隧道)
4. 也可以点「导入连接」导入 Navicat 的 `.ncx` 文件
5. 连接后左侧树浏览数据库和表
6. 双击表名查看数据,右键有更多操作(复制/删除/清空/转储)
7. 点击「新建查询」打开 SQL 编辑器
2. 点击工具栏「连接」创建 MySQL 连接,或用「导入连接」导入 Navicat 的 `.ncx` 文件
3. 填写主机、端口、用户名、密码,按需开启 SSH 隧道
4. 连接后在左侧树浏览数据库和表,双击表名查看数据
5. 右键数据库或表可打开导入向导、导出向导、复制表、转储 SQL、查看 DDL、修改表结构
6. 点击「新建查询」打开 SQL 编辑器,运行当前语句或选中的 SQL
7. 在右侧对象信息面板中查看 DDL、列、索引、外键信息,并直接做结构管理

@@ -97,2 +104,3 @@ ## 安全说明

- **后端**: Express 5 + mysql2 + ssh2
- **数据导入导出**: Papa Parse + xlsx + File System Access API
- **部署**: 单进程同时服务 API 和前端静态文件(无需 nginx)

@@ -99,0 +107,0 @@

@@ -19,2 +19,11 @@ import express from 'express'

import { importBatch } from './import.js'
import {
closeAllSshShellSessions,
closeSshShellSession,
executeSshCommand,
readSshShellSession,
resizeSshShellSession,
startSshShellSession,
writeSshShellInput,
} from './ssh.js'

@@ -164,2 +173,100 @@ const app = express()

app.post('/api/ssh/exec', async (request, response) => {
try {
const connection = normalizeConnectionPayload(request.body.connection)
const startedAt = Date.now()
const result = await executeSshCommand(connection, {
command: request.body.command,
timeoutMs: request.body.timeoutMs,
sshTarget: request.body.sshTarget,
})
response.json({
...result,
durationMs: Date.now() - startedAt,
})
} catch (error) {
console.error('[/api/ssh/exec] ERROR:', error)
response.status(400).json({
error: error instanceof Error ? error.message : 'SSH command failed.',
})
}
})
app.post('/api/ssh/session/start', async (request, response) => {
try {
const connection = normalizeConnectionPayload(request.body.connection)
const result = await startSshShellSession(connection, {
sshTarget: request.body.sshTarget,
cols: request.body.cols,
rows: request.body.rows,
term: request.body.term,
idleTimeoutMs: request.body.idleTimeoutMs,
})
response.json(result)
} catch (error) {
console.error('[/api/ssh/session/start] ERROR:', error)
response.status(400).json({
error: error instanceof Error ? error.message : 'SSH shell start failed.',
})
}
})
app.post('/api/ssh/session/read', (request, response) => {
try {
const result = readSshShellSession({
sessionId: request.body.sessionId,
afterSeq: request.body.afterSeq,
limit: request.body.limit,
})
response.json(result)
} catch (error) {
response.status(400).json({
error: error instanceof Error ? error.message : 'SSH shell read failed.',
})
}
})
app.post('/api/ssh/session/input', (request, response) => {
try {
const result = writeSshShellInput({
sessionId: request.body.sessionId,
input: request.body.input,
appendNewline: request.body.appendNewline,
})
response.json(result)
} catch (error) {
response.status(400).json({
error: error instanceof Error ? error.message : 'SSH shell input failed.',
})
}
})
app.post('/api/ssh/session/resize', (request, response) => {
try {
const result = resizeSshShellSession({
sessionId: request.body.sessionId,
cols: request.body.cols,
rows: request.body.rows,
})
response.json(result)
} catch (error) {
response.status(400).json({
error: error instanceof Error ? error.message : 'SSH shell resize failed.',
})
}
})
app.post('/api/ssh/session/close', (request, response) => {
try {
const result = closeSshShellSession({
sessionId: request.body.sessionId,
})
response.json(result)
} catch (error) {
response.status(400).json({
error: error instanceof Error ? error.message : 'SSH shell close failed.',
})
}
})
/* ── Data Import ────────────────────────────── */

@@ -220,2 +327,3 @@ app.post('/api/import/batch', async (request, response) => {

async function shutdown() {
closeAllSshShellSessions()
await closePools()

@@ -222,0 +330,0 @@ server.close(() => {

@@ -6,2 +6,6 @@ import type {

SchemaTreeResponse,
SshExecResponse,
SshShellSessionReadResponse,
SshShellSessionStartResponse,
SshTarget,
TableColumnsResponse,

@@ -74,2 +78,58 @@ TableMetadataResponse,

},
runSshCommand(
connection: ConnectionProfile,
command: string,
sshTarget?: SshTarget,
timeoutMs?: number,
) {
return request<SshExecResponse>('/ssh/exec', {
connection,
command,
sshTarget,
timeoutMs,
})
},
startSshShell(
connection: ConnectionProfile,
sshTarget?: SshTarget,
options?: {
cols?: number
rows?: number
term?: string
idleTimeoutMs?: number
},
) {
return request<SshShellSessionStartResponse>('/ssh/session/start', {
connection,
sshTarget,
cols: options?.cols,
rows: options?.rows,
term: options?.term,
idleTimeoutMs: options?.idleTimeoutMs,
})
},
readSshShell(sessionId: string, afterSeq?: number, limit?: number) {
return request<SshShellSessionReadResponse>('/ssh/session/read', {
sessionId,
afterSeq,
limit,
})
},
writeSshShellInput(sessionId: string, input: string, appendNewline = true) {
return request<{ ok: true; sessionId: string; nextSeq: number }>('/ssh/session/input', {
sessionId,
input,
appendNewline,
})
},
resizeSshShell(sessionId: string, cols: number, rows: number) {
return request<{ ok: true; sessionId: string }>('/ssh/session/resize', {
sessionId,
cols,
rows,
})
},
closeSshShell(sessionId: string) {
return request<{ ok: true }>('/ssh/session/close', { sessionId })
},
importBatch(

@@ -76,0 +136,0 @@ connection: ConnectionProfile,

@@ -129,2 +129,50 @@ /* ── Connection ──────────────────────────────── */

export type SshTarget = {
host?: string
port?: number
user?: string
}
export type SshExecResponse = {
host: string
port: number
user: string
command: string
stdout: string
stderr: string
exitCode: number | null
signal?: string
durationMs: number
truncated: boolean
}
export type SshShellChunk = {
seq: number
stream: 'stdout' | 'stderr' | 'system'
text: string
timestamp: number
}
export type SshShellSessionStartResponse = {
sessionId: string
host: string
port: number
user: string
nextSeq: number
createdAt: number
}
export type SshShellSessionReadResponse = {
sessionId: string
host: string
port: number
user: string
chunks: SshShellChunk[]
nextSeq: number
closed: boolean
exitCode: number | null
signal?: string
lastActiveAt: number
}
/* ── Tab system ─────────────────────────────── */

@@ -131,0 +179,0 @@

Sorry, the diff of this file is too big to display

:root{--bg-app: #ececec;--bg-toolbar: #e8e8e8;--bg-sidebar: #f5f5f5;--bg-main: #ffffff;--bg-info: #fafafa;--bg-statusbar: #e8e8e8;--bg-modal-overlay: rgba(0, 0, 0, .35);--border: #d4d4d4;--border-light: #e6e6e6;--border-dark: #bbb;--select-bg: #2968c8;--select-bg-hover: #e8f0fe;--select-text: #ffffff;--text: #333333;--text-secondary: #777777;--text-muted: #aaaaaa;--accent: #2968c8;--accent-hover: #1a54b0;--danger: #d93025;--success: #1e8e3e;--warning: #f9a825;--table-header-bg: #f0f0f0;--table-row-alt: #f9f9fc;--table-row-hover: #eef3fb;--table-border: #e2e2e2;--tab-active-bg: #ffffff;--tab-inactive-bg: #e8e8e8;--tab-border: #cccccc;--toolbar-h: 72px;--statusbar-h: 26px;--sidebar-w: 240px;--info-w: 360px;--tab-h: 32px;--font-sans: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", "Segoe UI", sans-serif;--font-mono: "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", monospace;--font-size: 13px;--font-size-sm: 13px;--font-size-xs: 12px}[data-theme=dark]{--bg-app: #1e1e1e;--bg-toolbar: #252526;--bg-sidebar: #1e1e1e;--bg-main: #1e1e1e;--bg-info: #252526;--bg-statusbar: #1e1e1e;--bg-modal-overlay: rgba(0, 0, 0, .6);--border: #3c3c3c;--border-light: #2d2d2d;--border-dark: #4a4a4a;--select-bg: #264f78;--select-bg-hover: #2a2d2e;--select-text: #ffffff;--text: #cccccc;--text-secondary: #969696;--text-muted: #5a5a5a;--accent: #0078d4;--accent-hover: #1a8aed;--danger: #f44747;--success: #4ec9b0;--warning: #cca700;--table-header-bg: #2d2d2d;--table-row-alt: #232323;--table-row-hover: #2a2d2e;--table-border: #3c3c3c;--tab-active-bg: #1e1e1e;--tab-inactive-bg: #2d2d2d;--tab-border: #3c3c3c;color-scheme:dark}[data-theme=dark] input,[data-theme=dark] select,[data-theme=dark] textarea{background:#2d2d2d;color:#ccc;border-color:#3c3c3c}[data-theme=dark] .history-panel,[data-theme=dark] .combo-box-dropdown{background:#252526}[data-theme=dark] .cli-container{background:#1b1b1b}[data-theme=dark] .cli-output{color:#ccc}[data-theme=dark] .cli-welcome{color:#5a5a5a;border-bottom-color:#3c3c3c}[data-theme=dark] .cli-result{color:#969696}[data-theme=dark] .import-error-list{background:#3a2020;border-color:#5a3030}[data-theme=dark] .toolbar-btn:hover{background:#ffffff14}[data-theme=dark] .toolbar-btn:active{background:#ffffff1f}[data-theme=dark] .toolbar-btn.active{background:#264f78;color:#6cb6ff}*,*:before,*:after{box-sizing:border-box}body{margin:0;font-family:var(--font-sans);font-size:var(--font-size);color:var(--text);background:var(--bg-app);overflow:hidden}button,input,textarea,select{font:inherit}button{cursor:pointer;border:none;background:none}ul,ol{list-style:none;margin:0;padding:0}#root{height:100vh}.app-shell{display:flex;flex-direction:column;height:100vh;overflow:hidden}.toolbar{height:var(--toolbar-h);background:var(--bg-toolbar);border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 12px;gap:2px;flex-shrink:0;-webkit-user-select:none;user-select:none}.toolbar-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;padding:6px 12px;border-radius:6px;color:var(--text);font-size:var(--font-size-xs);min-width:56px;transition:background .12s}.toolbar-btn:hover{background:#0000000f}.toolbar-btn:active{background:#0000001a}.toolbar-btn.active{background:#e8f0fe;color:#1a73e8}.toolbar-btn .tb-icon{font-size:24px;line-height:1;display:flex;align-items:center;justify-content:center;width:32px;height:32px}.toolbar-btn .tb-label{white-space:nowrap}.toolbar-sep{width:1px;height:44px;background:var(--border);margin:0 6px;flex-shrink:0}.toolbar-spacer{flex:1}.workspace{flex:1;display:flex;overflow:hidden;min-height:0}.sidebar{width:var(--sidebar-w);min-width:140px;background:var(--bg-sidebar);border-right:none;display:flex;flex-direction:column;flex-shrink:0;overflow:hidden}.panel-resize-handle.vertical{width:5px;cursor:col-resize;background:var(--border);flex-shrink:0;position:relative;transition:background .15s;z-index:5;align-self:stretch}.panel-resize-handle.vertical:before{content:"";position:absolute;inset:0 -3px;z-index:6}.panel-resize-handle.vertical:hover,.panel-resize-handle.vertical:active{background:var(--accent)}.panel-resize-handle.vertical:after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);height:30px;width:2px;border-left:1px solid rgba(0,0,0,.15);border-right:1px solid rgba(0,0,0,.15)}.sidebar-header{padding:8px 12px 6px;font-size:var(--font-size-sm);font-weight:600;color:var(--text-secondary);border-bottom:1px solid var(--border-light);display:flex;align-items:center;gap:6px;flex-shrink:0}.search-with-history-wrap{position:relative;flex:none;max-width:100%}.sidebar-search-wrap{margin:6px 8px;align-self:stretch}.sidebar-search{margin:0;padding:4px 8px;border:1px solid var(--border);border-radius:4px;font-size:var(--font-size-sm);background:#fff;outline:none;flex-shrink:0;width:100%}.sidebar-search:focus{border-color:var(--accent)}.sidebar-search::placeholder{color:var(--text-muted)}.search-history-dropdown{position:absolute;top:100%;left:0;right:0;z-index:100;background:#fff;border:1px solid var(--border);border-radius:6px;box-shadow:0 4px 12px #0000001f;margin-top:2px;overflow:hidden}.search-history-item{padding:6px 10px;font-size:12px;color:var(--text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.search-history-item:hover{background:var(--table-row-hover)}[data-theme=dark] .search-history-dropdown{background:#21262d;border-color:#30363d}.tree-container{flex:1;overflow-y:auto;overflow-x:hidden;padding:2px 0}.tree-node{display:flex;align-items:center;padding:2px 8px 2px 0;cursor:pointer;font-size:var(--font-size-sm);white-space:nowrap;-webkit-user-select:none;user-select:none;border:1px solid transparent;min-height:24px}.tree-node:hover{background:var(--select-bg-hover)}.tree-node.selected{background:var(--select-bg);color:var(--select-text)}.tree-node.selected:hover{background:var(--accent-hover)}.tree-arrow{width:16px;height:16px;display:flex;align-items:center;justify-content:center;font-size:10px;color:var(--text-secondary);flex-shrink:0;transition:transform .12s ease}.tree-arrow.expanded{transform:rotate(90deg)}.tree-node.selected .tree-arrow{color:var(--select-text)}.conn-color-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.tree-icon{width:18px;height:18px;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-right:4px;font-size:14px}.tree-label{overflow:hidden;text-overflow:ellipsis}.main-area{flex:1;display:flex;flex-direction:column;min-width:0;background:var(--bg-main);overflow:hidden}.tab-bar{display:flex;align-items:flex-end;background:var(--tab-inactive-bg);border-bottom:1px solid var(--border);flex-shrink:0;min-height:var(--tab-h);overflow-x:auto;overflow-y:hidden;-webkit-user-select:none;user-select:none}.tab-bar::-webkit-scrollbar{height:0}.tab-item{display:flex;align-items:center;gap:6px;padding:6px 12px;font-size:var(--font-size-sm);border:1px solid var(--tab-border);border-bottom:none;border-radius:4px 4px 0 0;background:var(--tab-inactive-bg);color:var(--text-secondary);margin-left:-1px;cursor:pointer;white-space:nowrap;max-width:200px;position:relative;transition:background .1s}.tab-item:first-child{margin-left:4px}.tab-item:hover{background:#f0f0f0}.tab-item.active{background:var(--tab-active-bg);color:var(--text);border-bottom:1px solid var(--tab-active-bg);z-index:1;font-weight:500}.tab-item .tab-icon{font-size:14px}.tab-item .tab-title{overflow:hidden;text-overflow:ellipsis}.tab-close{width:16px;height:16px;display:flex;align-items:center;justify-content:center;border-radius:3px;font-size:14px;color:var(--text-muted);flex-shrink:0;line-height:1}.tab-close:hover{background:#0000001a;color:var(--text)}.tab-item.active .tab-close{color:var(--text-secondary)}.tab-content{flex:1;display:flex;flex-direction:column;overflow:hidden;min-height:0}.empty-state{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;color:var(--text-muted);font-size:14px}.empty-state .empty-icon{font-size:48px;opacity:.4}.empty-state .empty-title{font-size:16px;color:var(--text-secondary);font-weight:500}.objects-toolbar{display:flex;align-items:center;padding:6px 8px;gap:6px;border-bottom:1px solid var(--border-light);flex-shrink:0}.objects-toolbar button{padding:3px 6px;border-radius:3px;font-size:var(--font-size-sm);color:var(--text-secondary)}.objects-toolbar button:hover{background:#0000000f}.objects-search-wrap{width:180px;flex-shrink:0}.objects-search{padding:3px 8px;border:1px solid var(--border);border-radius:4px;font-size:var(--font-size-sm);width:100%;background:#fff;outline:none;margin-left:0}.objects-search:focus{border-color:var(--accent)}.objects-view-toggle{display:flex;gap:2px}.view-btn{padding:4px 6px!important;font-size:14px!important}.view-btn.active{background:#00000014!important}.objects-grid{flex:1;overflow:auto}.objects-table{width:100%;border-collapse:collapse}.objects-table th{position:sticky;top:0;background:var(--table-header-bg);border-bottom:1px solid var(--table-border);padding:5px 12px;text-align:left;font-size:var(--font-size-sm);font-weight:500;color:var(--text-secondary);white-space:nowrap;z-index:1}.objects-table td{padding:4px 12px;border-bottom:1px solid var(--border-light);font-size:var(--font-size-sm);white-space:nowrap}.objects-table tr:hover td{background:var(--table-row-hover)}.objects-table .obj-icon{margin-right:6px;font-size:14px}.data-toolbar{display:flex;align-items:center;padding:4px 8px;gap:4px;border-bottom:1px solid var(--border-light);flex-shrink:0;background:var(--bg-info)}.data-toolbar button{padding:3px 8px;border-radius:3px;font-size:var(--font-size-sm);color:var(--text-secondary)}.data-toolbar button:hover{background:#0000000f}.filter-label{font-size:var(--font-size-xs);color:var(--text-secondary);white-space:nowrap}.filter-input{flex:1;max-width:300px;min-width:120px;padding:2px 8px;border:1px solid var(--border);border-radius:4px;font-size:var(--font-size-xs);font-family:var(--font-mono);outline:none;transition:border-color .15s}.filter-input:focus{border-color:var(--accent)}.filter-clear{background:none;border:none;color:var(--danger);cursor:pointer;font-size:12px;padding:2px 4px;border-radius:3px}.filter-clear:hover{background:#fee}.sort-indicator{font-size:11px;color:var(--accent);font-weight:700}.col-header{position:relative;cursor:default}.col-header-label{flex:1}.col-sort-trigger{opacity:0;font-size:10px;color:var(--fg-dim);cursor:pointer;padding:0 2px;transition:opacity .15s;-webkit-user-select:none;user-select:none}.col-header:hover .col-sort-trigger{opacity:1}.col-sort-menu{position:absolute;top:100%;left:0;z-index:200;background:#fff;border:1px solid var(--border);border-radius:6px;box-shadow:0 4px 16px #00000038;min-width:140px;padding:4px 0;font-size:13px;font-weight:400;white-space:nowrap}.col-sort-item{padding:6px 12px;cursor:pointer;display:flex;align-items:center;gap:2px}.col-sort-item:hover{background:var(--hover)}.col-sort-sep{height:1px;background:var(--border);margin:4px 0}.filter-tag{display:inline-flex;align-items:center;gap:4px;background:#e8f0fe;color:#1a73e8;font-size:12px;padding:2px 8px;border-radius:12px;margin-right:4px;white-space:nowrap}.filter-tag strong{font-weight:600}.filter-tag-close{background:none;border:none;color:#1a73e8;cursor:pointer;font-size:11px;padding:0 2px;line-height:1}.filter-tag-close:hover{color:#c62828}.filter-clear-all{background:none;border:1px solid var(--border);border-radius:4px;color:var(--fg-dim);font-size:11px;cursor:pointer;padding:2px 8px;margin-left:4px}.filter-clear-all:hover{background:var(--hover)}.toolbar-icon-btn{background:none;border:1px solid transparent;border-radius:4px;font-size:16px;cursor:pointer;padding:2px 6px}.toolbar-icon-btn:hover{background:var(--hover)}.toolbar-icon-btn.active{background:#e8f0fe;border-color:#4285f4}.navi-filter-panel{border-bottom:1px solid #d8d8d8;background:#fafafa}.navi-filter-conditions{padding:10px 14px;min-height:40px}.navi-filter-row{display:flex;align-items:center;gap:6px;padding:3px 0;font-size:13px}.navi-filter-check{width:16px;height:16px;margin:0;cursor:pointer;accent-color:#4285f4}.navi-filter-col-select{color:#06c;font-weight:600;font-size:13px;border:none;background:transparent;cursor:pointer;outline:none;padding:2px 4px;appearance:none;-webkit-appearance:none}.navi-filter-col-select:hover{text-decoration:underline}.navi-filter-op{color:#333;padding:1px 2px;border:1px solid #d0d0d0;border-radius:3px;background:#fff;font-size:12px;cursor:pointer;min-width:50px;outline:none}.navi-filter-val{color:#333;cursor:pointer;padding:2px 8px;background:#fff;border:1px solid #d0d0d0;border-radius:3px;min-width:20px;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.navi-filter-val:hover{border-color:#999}.navi-filter-connector{color:#999;font-size:12px;padding:0 4px}.navi-filter-btn-icon{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;font-size:14px;font-weight:700;color:#fff;background:#4285f4;border-radius:3px;cursor:pointer;margin-left:2px}.navi-filter-btn-icon:hover{background:#3367d6}.navi-filter-btn-group{background:#7baaf7;font-size:11px}.navi-filter-btn-group:hover{background:#5a94e6}.navi-filter-remove{background:none;border:none;color:#ccc;cursor:pointer;font-size:12px;padding:0 4px;margin-left:auto}.navi-filter-remove:hover{color:#c62828}.navi-filter-group{margin:2px 0}.navi-filter-group-paren{font-size:15px;font-weight:600;color:#555;padding:0 2px}.navi-filter-group-children{padding-left:24px}.navi-filter-toolbar{display:flex;align-items:center;gap:2px;padding:4px 14px;background:#f0f0f0;border-top:1px solid #e0e0e0}.navi-filter-tool{width:26px;height:24px;display:flex;align-items:center;justify-content:center;font-size:14px;color:#555;cursor:pointer;border-radius:4px}.navi-filter-tool:hover{background:#d6d6d6}.navi-filter-apply{font-size:13px;color:#333;cursor:pointer;padding:2px 10px;border-radius:4px;font-weight:500}.navi-filter-apply:hover{background:#d6d6d6}.navi-filter-status{font-size:12px;color:#999;margin-left:8px}.navi-picker-overlay{position:absolute;inset:0;z-index:300}.navi-picker{position:absolute;top:60px;right:40px;width:380px;max-height:440px;background:#f6f6f6;border-radius:10px;box-shadow:0 8px 32px #00000040;display:flex;flex-direction:column;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.navi-picker-input{margin:12px 14px 0;padding:7px 8px;border:2px solid #4a90d9;border-radius:4px;font-size:13px;outline:none;background:#fff}.navi-picker-input:focus{border-color:#2563eb}.navi-picker-section{padding:10px 14px 4px;font-size:13px;color:#666;font-weight:500}.navi-picker-list{flex:1;overflow-y:auto;min-height:60px;max-height:220px;margin:4px 10px;background:#fff;border:1px solid #d4d4d4;border-radius:6px}.navi-picker-loading{text-align:center;padding:20px;color:#999;font-size:13px}.navi-picker-item{display:flex;align-items:center;gap:8px;padding:5px 10px;cursor:pointer;font-size:13px;border-bottom:1px solid #f0f0f0;color:#333}.navi-picker-item:last-child{border-bottom:none}.navi-picker-item:hover{background:#f0f5ff}.navi-picker-item input[type=checkbox]{width:15px;height:15px;cursor:pointer;flex-shrink:0;margin:0}.navi-picker-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.navi-picker-search-row{display:flex;align-items:center;margin:6px 14px;border:1px solid #d4d4d4;border-radius:6px;background:#fff;overflow:hidden}.navi-picker-search-icon{padding:5px 6px 5px 10px;font-size:14px;color:#999;flex-shrink:0}.navi-picker-search{flex:1;border:none;outline:none;padding:6px 8px 6px 2px;font-size:13px;background:transparent}.navi-picker-actions{display:flex;justify-content:flex-end;gap:8px;padding:8px 14px 12px}.navi-picker-btn{padding:5px 22px;border:1px solid #c0c0c0;border-radius:6px;background:#fff;font-size:13px;cursor:pointer;color:#333}.navi-picker-btn:hover{background:#eaeaea}.navi-picker-btn-ok{background:#4285f4;color:#fff;border-color:#4285f4;font-weight:500}.navi-picker-btn-ok:hover{background:#3367d6}.data-grid-wrap{flex:1;overflow:auto;min-height:0}.data-grid{width:auto;border-collapse:collapse;table-layout:auto}.query-result-grid{width:max-content;table-layout:fixed}.data-grid th{top:0;background:var(--table-header-bg);border-bottom:2px solid var(--table-border);border-right:1px solid var(--table-border);padding:4px 10px;text-align:left;font-size:var(--font-size-sm);font-weight:600;white-space:nowrap;z-index:1;min-width:60px;position:sticky;overflow:visible}.col-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:5px;cursor:col-resize;z-index:3;-webkit-user-select:none;user-select:none}.col-resize-handle:hover,.col-resize-handle:active{background:var(--accent)}.data-grid td{padding:4px 10px;border-bottom:1px solid var(--border-light);border-right:1px solid var(--border-light);font-size:var(--font-size-xs);white-space:nowrap;max-width:400px;overflow:hidden;text-overflow:ellipsis}.data-grid tr:nth-child(2n) td{background:var(--table-row-alt)}.data-grid tr:hover td{background:var(--table-row-hover)}.data-grid tr.row-selected td{background:#cce5ff!important}.data-grid tr.row-selected:hover td{background:#b8daff!important}.data-grid tr.row-selected .row-num{background:#b8daff!important;color:#004085}.data-grid td.cell-selected{outline:2px solid #4285f4;outline-offset:-2px}.data-grid tr{cursor:default}.data-grid .row-num{color:var(--text-muted);text-align:right;background:var(--table-header-bg);border-right:2px solid var(--table-border);font-family:var(--font-mono);font-size:var(--font-size-xs);min-width:40px;padding:3px 6px;position:sticky;left:0;z-index:0}.data-grid th.row-num-header{min-width:40px;z-index:2;position:sticky;left:0}.cell-null{color:var(--text-muted);font-style:italic}.data-footer{display:flex;align-items:center;justify-content:space-between;padding:4px 8px;border-top:1px solid var(--border-light);background:var(--bg-info);font-size:var(--font-size-xs);color:var(--text-secondary);flex-shrink:0;gap:8px;flex-wrap:wrap}.data-footer-right{display:flex;align-items:center;gap:12px}.data-footer-sql{font-family:var(--font-mono);color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:50%}.cell-edit-input{width:100%;min-width:60px;padding:1px 4px;border:2px solid var(--accent);border-radius:2px;font-family:var(--font-mono);font-size:var(--font-size-xs);outline:none;background:#fff;box-sizing:border-box}.cell-modified{background:#fff3cd!important}.data-grid tr.row-new td{background:#d4edda!important}.data-grid tr.row-new .row-num{background:#c3e6cb!important;color:#155724}.data-grid tr.row-deleted td{background:#f8d7da!important;text-decoration:line-through;color:#999}.data-grid tr.row-deleted .row-num{background:#f5c6cb!important;color:#721c24}.data-action-bar{display:flex;align-items:center;gap:2px;flex-shrink:0}.action-btn{width:26px;height:22px;border:1px solid var(--border);border-radius:3px;background:#fff;cursor:pointer;font-size:14px;line-height:1;display:flex;align-items:center;justify-content:center;color:var(--text-secondary);transition:background .1s,color .1s}.action-btn:hover:not(:disabled){background:var(--table-row-hover);color:var(--text)}.action-btn:disabled{opacity:.35;cursor:default}.action-btn.apply{color:#28a745;font-weight:700}.action-btn.apply:hover:not(:disabled){background:#d4edda}.action-btn.cancel{color:#dc3545;font-weight:700}.action-btn.cancel:hover:not(:disabled){background:#f8d7da}.action-status{font-size:var(--font-size-xs);color:var(--text-secondary);margin-left:8px}.pagination{display:flex;align-items:center;gap:2px}.page-btn{padding:2px 8px!important;border:1px solid var(--border)!important;border-radius:3px!important;font-size:11px!important;background:#fff!important;cursor:pointer!important;line-height:1.4!important;min-width:24px!important;text-align:center!important}.page-btn:hover{background:var(--select-bg-hover)!important}.page-btn:disabled{opacity:.35;cursor:default!important;background:#f5f5f5!important}.page-info{padding:0 6px;font-size:11px;color:var(--text-secondary);white-space:nowrap}.page-input{width:38px;padding:1px 4px;margin:0 4px;border:1px solid var(--border);border-radius:3px;font-size:11px;font-weight:600;color:var(--accent);text-align:center;background:#fff;outline:none;line-height:1.4}.page-input:focus{border-color:var(--accent);box-shadow:0 0 0 2px #2968c826}.page-count{font-size:11px;color:var(--text-secondary);white-space:nowrap;padding-left:4px;border-left:1px solid var(--border-light)}.query-pane{flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}.query-toolbar{display:flex;align-items:center;padding:2px 8px;gap:2px;border-bottom:1px solid var(--border-light);flex-shrink:0;background:var(--bg-info);height:32px}.query-toolbar select{padding:2px 6px;border:1px solid var(--border);border-radius:3px;font-size:var(--font-size-sm);background:var(--bg-main);color:var(--text)}.query-db-selector{display:flex;align-items:center;gap:4px}.query-db-selector select{padding:2px 20px 2px 6px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:var(--bg-main);min-width:140px;cursor:pointer;appearance:auto;color:var(--text);height:24px}.query-db-selector select:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px #2968c826}.tb-icon-btn{display:flex;align-items:center;justify-content:center;width:28px;height:24px;border:none;background:transparent;color:var(--text-muted, #666);border-radius:4px;cursor:pointer;padding:0;transition:all .12s ease}.tb-icon-btn:hover{background:#0000000f;color:var(--text)}.tb-icon-btn:active{background:#0000001a}.tb-icon-btn:disabled{opacity:.3;cursor:default}.tb-icon-btn:disabled:hover{background:transparent}.tb-icon-btn.tb-run{color:#2ea043}.tb-icon-btn.tb-run:hover{background:#2ea0431a;color:#238636}.toolbar-sep{width:1px;height:16px;background:var(--border);margin:0 4px;flex-shrink:0}.run-btn{padding:3px 12px!important;background:var(--accent)!important;color:#fff!important;border-radius:4px!important;font-weight:500!important;font-size:var(--font-size-sm)!important}.run-btn:hover{background:var(--accent-hover)!important}.run-btn:disabled{opacity:.5;cursor:not-allowed}.run-btn.stop{background:#d73a49!important}.run-btn.stop:hover{background:#cb2431!important}.query-split-container{flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}.query-editor-area{min-height:60px;height:200px;flex-shrink:0;position:relative;overflow:hidden}.query-editor-area .cm-editor,.query-editor-area .cm-scroller,.query-editor-area .cm-content,.query-editor-area .cm-line{user-select:text!important;-webkit-user-select:text!important}.query-split-handle{height:4px;background:var(--border-light);cursor:row-resize;flex-shrink:0;position:relative;transition:background .15s}.query-split-handle:hover,.query-split-handle:active{background:var(--accent)}.query-split-handle:after{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:30px;height:2px;border-top:1px solid rgba(0,0,0,.15);border-bottom:1px solid rgba(0,0,0,.15)}.query-results{flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden;contain:strict;isolation:isolate}.result-view-tabs{display:flex;align-items:stretch;background:var(--bg-info);border-bottom:1px solid var(--border-light);flex-shrink:0;gap:0}.result-view-actions{display:flex;align-items:center;gap:6px;padding:0 8px}.result-action-btn{min-width:38px;height:26px;padding:0 10px;border:1px solid var(--border);border-radius:6px;background:#fff;cursor:pointer;font-size:12px;line-height:1;font-weight:600;display:inline-flex;align-items:center;justify-content:center;color:var(--text-secondary);transition:background .12s,color .12s,border-color .12s,box-shadow .12s;white-space:nowrap}.result-action-btn:hover{background:var(--table-row-hover);color:var(--text);border-color:var(--accent-soft)}.result-action-btn:focus-visible{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px #2968c829}.result-action-btn-icon{min-width:30px;width:30px;padding:0;font-size:13px}.result-view-tab{padding:6px 16px;font-size:var(--font-size-xs);color:var(--text-secondary);border:1px solid transparent;border-bottom:none;border-radius:4px 4px 0 0;cursor:pointer;background:none;margin-bottom:-1px;transition:all .12s}.result-view-tab:hover{color:var(--text);background:#00000008}.result-view-tab.active{color:var(--accent);background:#fff;border-color:var(--border-light);font-weight:500}.result-grid-wrap{flex:1;display:flex;flex-direction:column;overflow:hidden;min-height:0;contain:layout paint}.virtual-grid-scroll{contain:layout paint;isolation:isolate;will-change:scroll-position}.query-messages{padding:12px 16px;font-family:var(--font-mono);font-size:var(--font-size-xs);line-height:1.7;color:var(--text);overflow:auto}.query-message-item{margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid var(--border-light)}.query-message-item:last-child{border-bottom:none;margin-bottom:0}.qm-sql{color:var(--text);word-break:break-all}.qm-info{color:#666}.qm-time{color:#999}.info-panel{width:clamp(240px,20vw,500px);background:var(--bg-info);border-left:none;display:flex;flex-direction:column;flex-shrink:0;overflow:hidden}.info-header{padding:16px 16px 12px;border-bottom:1px solid var(--border-light);display:flex;align-items:center;gap:12px;flex-shrink:0}.info-icon{width:48px;height:48px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:28px;flex-shrink:0}.info-icon.conn-icon{background:linear-gradient(135deg,#e8d5b7,#c9a96e)}.info-icon.db-icon{background:linear-gradient(135deg,#b8e6c8,#5cb87a)}.info-icon.table-icon{background:linear-gradient(135deg,#b8d4f0,#5a9fd4)}.info-title{font-size:16px;font-weight:600;line-height:1.2}.info-subtitle{font-size:var(--font-size-xs);color:var(--text-muted);margin-top:2px}.info-body{flex:1;overflow-y:auto;padding:12px 16px}.info-row{display:flex;flex-direction:column;gap:2px;margin-bottom:10px}.info-label{font-size:var(--font-size-xs);color:var(--text-muted);font-weight:500}.info-value{font-size:var(--font-size-sm);color:var(--text);word-break:break-all}.info-tab-bar{display:flex;padding:0 8px;border-bottom:1px solid var(--border-light);flex-shrink:0;background:var(--bg-info);gap:0}.info-tab{padding:6px 12px;font-size:var(--font-size-xs);color:var(--text-secondary);border:none;border-bottom:2px solid transparent;background:none;cursor:pointer;border-radius:0;font-weight:500;transition:color .12s,border-color .12s}.info-tab:hover{color:var(--text)}.info-tab.active{color:var(--accent);border-bottom-color:var(--accent)}.ddl-area{margin-top:0;padding:12px;background:#f8f8f8;border:none;border-radius:0;font-family:var(--font-mono);font-size:var(--font-size-xs);line-height:1.6;white-space:pre;overflow:auto;color:var(--text);-webkit-user-select:text;user-select:text}.sql-kw{color:#1a0dab}.sql-type{color:#0b7285}.sql-str{color:#e65100}.sql-num{color:#6a1b9a}.sql-comment{color:#6a9955;font-style:italic}.sql-punct{color:#777}.ddl-body{padding:0!important}.ddl-body .ddl-area{flex:1;max-height:none;height:100%}[data-theme=dark] .ddl-area{background:#1e1e1e}[data-theme=dark] .sql-kw{color:#569cd6}[data-theme=dark] .sql-type{color:#4ec9b0}[data-theme=dark] .sql-str{color:#ce9178}[data-theme=dark] .sql-num{color:#b5cea8}[data-theme=dark] .sql-comment{color:#6a9955}[data-theme=dark] .sql-punct{color:gray}.columns-body{padding:0!important;overflow:auto}.info-inline-actions{display:flex;justify-content:flex-end;gap:8px;padding:8px;border-bottom:1px solid var(--border-light);background:var(--bg-info)}.info-columns-table{width:100%;border-collapse:collapse;font-size:var(--font-size-xs)}.info-columns-table th{position:sticky;top:0;background:var(--table-header-bg);border-bottom:1px solid var(--table-border);padding:4px 8px;text-align:left;font-weight:600;color:var(--text-secondary);white-space:nowrap;z-index:1}.info-columns-table td{padding:3px 8px;border-bottom:1px solid var(--border-light);white-space:nowrap}.info-columns-table tr:hover td{background:var(--table-row-hover)}.info-columns-table .col-name{font-weight:500;font-family:var(--font-mono);color:var(--text)}.info-columns-table .col-type{color:var(--accent);font-family:var(--font-mono)}.info-columns-table .col-key{color:var(--warning);font-weight:600}.status-bar{height:var(--statusbar-h);background:var(--bg-statusbar);border-top:1px solid var(--border);display:flex;align-items:center;padding:0 12px;gap:16px;font-size:var(--font-size-xs);color:var(--text-secondary);flex-shrink:0;-webkit-user-select:none;user-select:none}.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block;margin-right:4px}.status-dot.online{background:var(--success)}.status-dot.offline{background:var(--text-muted)}.modal-overlay{position:fixed;inset:0;background:var(--bg-modal-overlay);display:flex;align-items:center;justify-content:center;z-index:1000;animation:fade-in .15s ease-out}.modal-card{width:560px;max-height:80vh;background:#fff;border-radius:8px;box-shadow:0 12px 40px #00000040;display:flex;flex-direction:column;animation:slide-up .2s ease-out}.modal-title-bar{padding:14px 20px 12px;border-bottom:1px solid var(--border-light);display:flex;align-items:center;justify-content:space-between}.modal-title-bar h3{margin:0;font-size:14px;font-weight:600}.modal-close{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:4px;font-size:18px;color:var(--text-secondary)}.modal-close:hover{background:#0000000f}.modal-tabs{display:flex;padding:0 20px;border-bottom:1px solid var(--border-light)}.modal-tab{padding:8px 14px;font-size:var(--font-size-sm);color:var(--text-secondary);border-bottom:2px solid transparent}.modal-tab:hover{color:var(--text)}.modal-tab.active{color:var(--accent);border-bottom-color:var(--accent);font-weight:500}.modal-body{padding:20px;flex:1;overflow-y:auto}.modal-row{display:grid;grid-template-columns:80px 1fr;align-items:center;gap:8px;margin-bottom:12px}.modal-row label{font-size:var(--font-size-sm);color:var(--text-secondary);text-align:right}.modal-row input{padding:6px 10px;border:1px solid var(--border);border-radius:4px;font-size:var(--font-size-sm);outline:none;background:#fff}.modal-row input:focus{border-color:var(--accent)}.modal-row-split{display:grid;grid-template-columns:80px 1fr 60px 1fr;align-items:center;gap:8px;margin-bottom:12px}.modal-row-split label{font-size:var(--font-size-sm);color:var(--text-secondary);text-align:right}.modal-row-split input{padding:6px 10px;border:1px solid var(--border);border-radius:4px;font-size:var(--font-size-sm);outline:none;background:#fff}.modal-row-split input:focus{border-color:var(--accent)}.modal-diagram{display:flex;align-items:center;justify-content:center;gap:20px;padding:12px 0 20px}.modal-diagram-node{display:flex;flex-direction:column;align-items:center;gap:4px;font-size:var(--font-size-sm);color:var(--text-secondary)}.modal-diagram-icon{font-size:32px}.modal-diagram-line{width:40px;height:2px;background:var(--border)}.modal-checkbox{display:flex;align-items:center;gap:8px;margin-bottom:12px;padding-left:88px}.modal-checkbox input{width:16px;height:16px}.modal-checkbox span{font-size:var(--font-size-sm);color:var(--text-secondary)}.modal-footer{padding:12px 20px;border-top:1px solid var(--border-light);display:flex;align-items:center;justify-content:space-between;gap:8px}.modal-footer-left,.modal-footer-right{display:flex;gap:8px}.btn{padding:6px 16px;border-radius:4px;font-size:var(--font-size-sm);font-weight:500;border:1px solid var(--border);background:#fff;color:var(--text);transition:background .1s}.btn:hover{background:#f0f0f0}.btn-primary{background:var(--accent);color:#fff;border-color:var(--accent)}.btn-primary:hover{background:var(--accent-hover)}.btn-danger{color:var(--danger);border-color:#d930254d}.btn-danger:hover{background:#d930250f}.btn-sm{padding:3px 10px;font-size:var(--font-size-xs)}.btn:disabled{opacity:.5;cursor:default}.btn-link{color:var(--accent);font-size:var(--font-size-sm);padding:4px 8px}.btn-link:hover{text-decoration:underline}.profiles-section{margin-top:16px;border-top:1px solid var(--border-light);padding-top:12px}.profiles-section h4{margin:0 0 8px;font-size:var(--font-size-sm);font-weight:600;color:var(--text-secondary)}.profile-item{display:flex;align-items:center;justify-content:space-between;padding:6px 8px;border-radius:4px;cursor:pointer;font-size:var(--font-size-sm)}.profile-item:hover{background:var(--select-bg-hover)}.profile-item .profile-name{font-weight:500}.profile-item .profile-host{color:var(--text-muted);font-size:var(--font-size-xs)}.profile-actions{display:flex;gap:4px}@keyframes slide-up{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}.resize-handle{width:4px;cursor:col-resize;background:transparent;transition:background .2s;flex-shrink:0}.resize-handle:hover{background:var(--accent)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#ccc;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#aaa}.context-menu-overlay{position:fixed;inset:0;z-index:800}.context-menu{position:fixed;z-index:801;min-width:200px;max-height:calc(100vh - 16px);overflow-y:auto;background:#fffffff5;-webkit-backdrop-filter:blur(20px) saturate(180%);backdrop-filter:blur(20px) saturate(180%);border:1px solid rgba(0,0,0,.12);border-radius:8px;box-shadow:0 8px 32px #0000002e,0 2px 8px #00000014;padding:4px 0;animation:ctx-pop .12s ease-out;font-size:var(--font-size-sm);-webkit-user-select:none;user-select:none}.ctx-item{display:flex;align-items:center;gap:8px;padding:6px 16px 6px 12px;cursor:pointer;color:var(--text);border-radius:4px;margin:0 4px;white-space:nowrap}.ctx-item:hover{background:var(--select-bg);color:#fff}.ctx-item.danger{color:var(--danger)}.ctx-item.danger:hover{background:var(--danger);color:#fff}.ctx-item.disabled{color:var(--text-muted);cursor:default}.ctx-item.disabled:hover{background:none;color:var(--text-muted)}.ctx-item .ctx-icon{width:18px;text-align:center;font-size:14px;flex-shrink:0}.ctx-item .ctx-label{flex:1}.ctx-item .ctx-shortcut{color:var(--text-muted);font-size:var(--font-size-xs);margin-left:16px}.ctx-item:hover .ctx-shortcut{color:#ffffffb3}.ctx-item.has-submenu{position:relative}.ctx-submenu{display:none;position:absolute;left:100%;top:-4px;min-width:200px;background:var(--bg-info);border:1px solid var(--border);border-radius:8px;padding:4px 0;box-shadow:0 6px 24px #00000026;z-index:1100}.ctx-item.has-submenu:hover>.ctx-submenu{display:block}.ctx-sep{height:1px;background:#00000014;margin:4px 8px}@keyframes ctx-pop{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}[data-theme=dark] .context-menu{background:#2d2d2df7;border-color:#4a4a4a;box-shadow:0 8px 32px #00000080}[data-theme=dark] .ctx-sep{background:#ffffff14}[data-theme=dark] .ctx-submenu{background:#2d2d2df7;border-color:#4a4a4a;box-shadow:0 8px 32px #00000080}[data-theme=dark] .context-menu-overlay{background:transparent}[data-theme=dark] .cm-editor{background:#1e1e1e!important}[data-theme=dark] .cm-editor .cm-content{caret-color:#aeafad}[data-theme=dark] .cm-editor .cm-cursor{border-left-color:#aeafad!important}[data-theme=dark] .cm-editor .cm-activeLine{background:#2a2a2a!important}[data-theme=dark] .cm-editor .cm-gutters{background:#1e1e1e!important;border-right-color:#3c3c3c!important;color:#5a5a5a!important}[data-theme=dark] .cm-editor .cm-activeLineGutter{background:#2a2a2a!important;color:#ccc!important}[data-theme=dark] .cm-editor .cm-selectionBackground{background:#264f78!important}[data-theme=dark] .cm-editor .cm-matchingBracket{background:#3a5a3a!important;outline-color:#5a8a5a!important}[data-theme=dark] .cm-editor .cm-panels{background:#252526!important;border-bottom-color:#3c3c3c!important}[data-theme=dark] .cm-editor .cm-tooltip{background:#252526!important;border-color:#4a4a4a!important}[data-theme=dark] .cm-editor .cm-tooltip-autocomplete ul>li[aria-selected]{background:#264f78!important}[data-theme=dark] .cm-editor .cm-completionDetail{color:#6a6a6a!important}[data-theme=dark] .result-view-tab.active{background:var(--bg-main);color:#6cb6ff;border-color:var(--border-light)}[data-theme=dark] .result-view-tab:hover{background:#ffffff0d}[data-theme=dark] .qm-info{color:#8b949e}[data-theme=dark] .qm-time{color:#6e7681}[data-theme=dark] .query-split-handle{background:#30363d}[data-theme=dark] .query-split-handle:after{border-top-color:#ffffff26;border-bottom-color:#ffffff26}[data-theme=dark] .data-grid th{background:#21262d;color:#c9d1d9;border-color:#30363d}[data-theme=dark] .data-grid td{border-color:#21262d}[data-theme=dark] .data-grid tr:nth-child(2n) td{background:#ffffff05}[data-theme=dark] .data-grid tr:hover td{background:#ffffff0f}[data-theme=dark] .data-grid tr.row-selected td{background:#1c3a5c!important}[data-theme=dark] .data-grid tr.row-selected:hover td{background:#214a6e!important}[data-theme=dark] .data-grid tr.row-selected .row-num{background:#214a6e!important;color:#6cb6ff}[data-theme=dark] .data-grid .row-num{background:#161b22;color:#484f58;border-color:#21262d}[data-theme=dark] .data-grid td.cell-selected{outline-color:#58a6ff}[data-theme=dark] .col-sort-popup{background:#21262d;border-color:#30363d}[data-theme=dark] .col-sort-item:hover{background:#ffffff14}[data-theme=dark] .page-btn{color:#8b949e;border-color:#30363d}[data-theme=dark] .page-btn:hover:not(:disabled){background:#ffffff14}[data-theme=dark] .page-input{background:#0d1117;color:#c9d1d9;border-color:#30363d}[data-theme=dark] .page-count{color:#6e7681}[data-theme=dark] .summary-kv{border-color:#21262d}[data-theme=dark] .summary-kv .sk-label{color:#8b949e}[data-theme=dark] .summary-kv .sk-value{color:#c9d1d9}[data-theme=dark] .query-toolbar{background:#161b22;border-bottom-color:#30363d}[data-theme=dark] .tb-icon-btn{color:#8b949e}[data-theme=dark] .tb-icon-btn:hover{background:#ffffff14;color:#c9d1d9}[data-theme=dark] .tb-icon-btn:active{background:#ffffff1f}[data-theme=dark] .tb-icon-btn.tb-run{color:#3fb950}[data-theme=dark] .tb-icon-btn.tb-run:hover{background:#3fb95026;color:#3fb950}[data-theme=dark] .toolbar-sep{background:#30363d}[data-theme=dark] .run-btn{background:#238636!important;color:#fff!important}[data-theme=dark] .run-btn:hover{background:#2ea043!important}[data-theme=dark] .run-btn.stop{background:#da3633!important}[data-theme=dark] .run-btn.stop:hover{background:#f85149!important}[data-theme=dark] .query-toolbar select,[data-theme=dark] .query-db-selector select{background:#21262d;color:#c9d1d9;border-color:#30363d}[data-theme=dark] .query-db-selector select:focus{border-color:#58a6ff;box-shadow:0 0 0 2px #58a6ff33}[data-theme=dark] .result-view-tab.active{background:#0d1117;border-color:#30363d}[data-theme=dark] .history-panel{background:#161b22;border-color:#30363d}[data-theme=dark] .history-item{border-bottom-color:#21262d}[data-theme=dark] .history-item:hover{background:#ffffff0a}[data-theme=dark] .history-panel-header{border-bottom-color:#30363d}[data-theme=dark] .empty-state{color:#484f58}[data-theme=dark] .tab-bar{background:#161b22;border-bottom-color:#30363d}[data-theme=dark] .tab-item{color:#8b949e;border-color:transparent}[data-theme=dark] .tab-item:hover{background:#ffffff0a}[data-theme=dark] .tab-item.active{background:var(--bg-main);color:#c9d1d9;border-color:#30363d;border-bottom-color:var(--bg-main)}[data-theme=dark] .tab-close:hover{background:#ffffff1a}[data-theme=dark] .action-bar{background:#161b22;border-bottom-color:#30363d}[data-theme=dark] .action-btn{color:#8b949e}[data-theme=dark] .action-btn:hover{background:#ffffff14;color:#c9d1d9}[data-theme=dark] .pagination-bar{background:#161b22;border-top-color:#30363d}.notice-toast{position:fixed;top:calc(var(--toolbar-h) + 8px);right:12px;padding:8px 16px;border-radius:6px;font-size:var(--font-size-sm);z-index:900;animation:slide-in-right .2s ease-out;max-width:400px;box-shadow:0 4px 16px #00000026}.notice-toast.success{background:#e6f4ea;color:#137333;border:1px solid #a8dab5}.notice-toast.error{background:#fce8e6;color:#c5221f;border:1px solid #f5c6cb}@keyframes slide-in-right{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}.history-panel{position:absolute;top:100%;left:0;width:480px;max-height:360px;background:#fff;border:1px solid var(--border);border-radius:8px;box-shadow:0 8px 30px #00000026;z-index:100;display:flex;flex-direction:column;overflow:hidden;margin-top:4px}.history-panel-header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;border-bottom:1px solid var(--border-light);font-size:12px;font-weight:600}.history-panel-header button{background:none;border:none;cursor:pointer;font-size:14px;padding:2px 4px;border-radius:4px}.history-panel-header button:hover{background:#0000000f}.history-empty{padding:24px;text-align:center;font-size:12px;color:var(--text-muted)}.history-list{overflow-y:auto;flex:1}.history-item{padding:6px 12px;cursor:pointer;border-bottom:1px solid var(--border-light);transition:background 80ms}.history-item:hover{background:var(--select-bg-hover)}.history-item:last-child{border-bottom:none}.history-item code{font-family:var(--font-mono);font-size:11px;color:var(--text);white-space:pre-wrap;word-break:break-all;line-height:1.4}.cli-container{height:100%;display:flex;flex-direction:column;background:#fafbfc;cursor:text}.cli-output{flex:1;overflow-y:auto;padding:12px 16px;font-family:SF Mono,Monaco,Menlo,Consolas,monospace;font-size:13px;line-height:1.5;color:#333}.cli-welcome{color:#888;padding-bottom:8px;border-bottom:1px solid #e0e0e0;margin-bottom:8px;font-size:12px}.cli-entry{margin-bottom:6px}.cli-prompt-line{display:flex;align-items:baseline}.cli-prompt{color:#2e7d32;font-weight:600;flex-shrink:0;-webkit-user-select:none;user-select:none}.cli-cmd{color:#1565c0}.cli-result{margin:2px 0 0;white-space:pre;font-family:inherit;font-size:12px;color:#444;overflow-x:auto}.cli-result.cli-error{color:#c62828}.cli-input-line{display:flex;align-items:center;padding-top:2px}.cli-input{flex:1;background:transparent;border:none;outline:none;color:#1565c0;font-family:inherit;font-size:13px;caret-color:#333}.cli-input::placeholder{color:#aaa}.cli-input:disabled{opacity:.5}.import-wizard-overlay{position:fixed;inset:0;background:var(--bg-modal-overlay);z-index:1000;display:flex;align-items:center;justify-content:center;animation:fade-in .15s ease-out}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.import-wizard-modal{background:var(--bg-main);border-radius:10px;box-shadow:0 20px 60px #0000004d;width:760px;max-width:90vw;max-height:85vh;display:flex;flex-direction:column;overflow:hidden;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:translateY(20px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}.import-wizard-header{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--border-light);flex-shrink:0}.import-wizard-title{font-size:15px;font-weight:600}.import-close-btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:6px;font-size:14px;color:var(--text-secondary)}.import-close-btn:hover{background:#00000014}.import-step-indicator{display:flex;align-items:center;justify-content:center;gap:4px;padding:14px 20px 10px;flex-shrink:0}.import-step-dot{display:flex;flex-direction:column;align-items:center;gap:4px;flex:1;position:relative}.import-step-dot:after{content:"";position:absolute;top:12px;left:calc(50% + 14px);right:calc(-50% + 14px);height:2px;background:var(--border)}.import-step-dot:last-child:after{display:none}.import-step-dot.done:after{background:var(--accent)}.import-step-dot-circle{width:24px;height:24px;border-radius:50%;background:var(--border-light);color:var(--text-secondary);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:600;z-index:1;transition:all .2s}.import-step-dot.active .import-step-dot-circle{background:var(--accent);color:#fff;box-shadow:0 0 0 3px #2968c833}.import-step-dot.done .import-step-dot-circle{background:var(--success);color:#fff}.import-step-dot-label{font-size:11px;color:var(--text-muted);white-space:nowrap}.import-step-dot.active .import-step-dot-label{color:var(--accent);font-weight:500}.import-step-dot.done .import-step-dot-label{color:var(--success)}.import-wizard-body{flex:1;overflow-y:auto;padding:16px 24px;min-height:300px}.import-step-title{font-size:15px;font-weight:600;margin-bottom:4px}.import-step-desc{font-size:12px;color:var(--text-secondary);margin-bottom:14px}.import-dropzone{border:2px dashed var(--border);border-radius:10px;padding:32px 20px;text-align:center;cursor:pointer;transition:border-color .2s,background .2s;margin-bottom:14px}.import-dropzone:hover{border-color:var(--accent);background:#2968c80a}.import-dropzone-hint{display:flex;flex-direction:column;align-items:center;gap:6px;color:var(--text-secondary);font-size:13px}.import-file-info{display:flex;align-items:center;justify-content:center;gap:8px;font-size:13px}.import-file-icon{font-size:24px}.import-file-name{font-weight:500}.import-file-size{color:var(--text-muted);font-size:12px}.import-options-row{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-secondary);flex-wrap:wrap}.import-options-row select{padding:3px 6px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:#fff}.import-options-row input[type=checkbox]{margin:0;accent-color:var(--accent)}.import-preview-table-wrap{max-height:340px;overflow:auto;border:1px solid var(--border-light);border-radius:6px}.import-preview-table{width:100%;border-collapse:collapse;font-size:12px}.import-preview-table th{position:sticky;top:0;background:var(--table-header-bg);padding:5px 10px;text-align:left;font-weight:500;color:var(--text-secondary);border-bottom:1px solid var(--table-border);white-space:nowrap;z-index:1}.import-preview-table td{padding:3px 10px;border-bottom:1px solid var(--border-light);white-space:nowrap;max-width:200px;overflow:hidden;text-overflow:ellipsis}.import-preview-table .import-row-num{color:var(--text-muted);text-align:center;width:36px;font-size:11px}.import-preview-table .cell-null{color:var(--text-muted);font-style:italic}.import-preview-hint{font-size:11px;color:var(--text-muted);text-align:center;padding:6px 0}.import-target-form{display:flex;flex-direction:column;gap:12px}.import-form-row{display:flex;align-items:center;gap:8px}.import-form-row label{width:80px;font-size:13px;font-weight:500;flex-shrink:0;text-align:right}.import-form-row select{flex:1;padding:6px 10px;border:1px solid var(--border);border-radius:5px;font-size:13px;background:#fff}.import-loading{font-size:12px;color:var(--accent)}.import-mapping-table-wrap{max-height:360px;overflow:auto;border:1px solid var(--border-light);border-radius:6px}.import-mapping-table{width:100%;border-collapse:collapse;font-size:12px}.import-mapping-table th{position:sticky;top:0;background:var(--table-header-bg);padding:6px 10px;text-align:left;font-weight:500;color:var(--text-secondary);border-bottom:1px solid var(--table-border);z-index:1}.import-mapping-table td{padding:4px 10px;border-bottom:1px solid var(--border-light)}.import-mapping-table code{background:#0000000a;padding:1px 5px;border-radius:3px;font-family:var(--font-mono);font-size:12px}.import-mapping-table select{width:100%;padding:4px 6px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:#fff}.import-mapping-skipped{opacity:.4}.import-preview-cell{max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-secondary);font-family:var(--font-mono);font-size:11px}.import-mapping-summary{font-size:12px;color:var(--text-secondary);text-align:right;padding:8px 0 0}.import-stats-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px}.import-stat{background:var(--bg-info);border:1px solid var(--border-light);border-radius:8px;padding:12px;text-align:center}.import-stat-label{font-size:11px;color:var(--text-secondary);margin-bottom:4px}.import-stat-value{font-size:20px;font-weight:600}.import-progress-bar-wrap{height:8px;background:var(--border-light);border-radius:4px;overflow:hidden;margin-bottom:6px}.import-progress-bar{height:100%;background:linear-gradient(90deg,var(--accent),#4fc3f7);border-radius:4px;transition:width .3s ease}.import-progress-text{font-size:12px;color:var(--text-secondary);text-align:center}.import-error-log{margin-top:14px}.import-error-list{max-height:120px;overflow-y:auto;border:1px solid #f5c6cb;border-radius:6px;background:#fef5f5;padding:8px 12px}.import-error-item{font-size:11px;color:#c62828;padding:2px 0;font-family:var(--font-mono)}.import-wizard-footer{display:flex;align-items:center;padding:12px 20px;border-top:1px solid var(--border-light);gap:8px;flex-shrink:0}.import-btn{padding:7px 18px;border-radius:6px;font-size:13px;font-weight:500;border:none;cursor:pointer;transition:background .12s}.import-btn:disabled{opacity:.4;cursor:not-allowed}.import-btn-primary{background:var(--accent);color:#fff}.import-btn-primary:hover:not(:disabled){background:var(--accent-hover)}.import-btn-secondary{background:var(--border-light);color:var(--text)}.import-btn-secondary:hover:not(:disabled){background:var(--border)}.combo-box-input{width:100%;padding:6px 10px;border:1px solid var(--border);border-radius:5px;font-size:13px;background:#fff;outline:none;transition:border-color .15s}.combo-box-input:focus{border-color:var(--accent)}.combo-box-dropdown{position:absolute;top:100%;left:0;right:0;z-index:100;background:#fff;border:1px solid var(--border);border-top:none;border-radius:0 0 6px 6px;box-shadow:0 6px 20px #00000026;max-height:240px;overflow-y:auto}.combo-box-item{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;font-size:12px;cursor:pointer;transition:background 80ms}.combo-box-item:hover{background:var(--select-bg-hover)}.combo-box-item.selected{background:var(--select-bg);color:var(--select-text)}.combo-box-item.combo-box-new{color:var(--accent);border-bottom:1px solid var(--border-light)}.combo-box-meta{font-size:11px;color:var(--text-muted)}.combo-box-item.selected .combo-box-meta{color:#ffffffb3}.combo-box-empty{padding:8px 10px;font-size:12px;color:var(--text-muted);text-align:center}.export-step-title{font-size:15px;font-weight:600;margin-bottom:4px}.export-step-desc{font-size:12px;color:var(--text-secondary);margin-bottom:14px}.export-format-list{display:flex;flex-direction:column;gap:6px}.export-format-item{display:flex;align-items:center;gap:10px;padding:10px 14px;border:1px solid var(--border-light);border-radius:8px;cursor:pointer;transition:border-color .15s,background .15s}.export-format-item:hover{border-color:var(--border);background:#00000004}.export-format-item.selected{border-color:var(--accent);background:#2968c80a}.export-format-item input[type=radio]{accent-color:var(--accent);margin:0}.export-format-info{display:flex;flex-direction:column;gap:2px}.export-format-label{font-size:13px;font-weight:500}.export-format-desc{font-size:11px;color:var(--text-muted)}.export-form-row{display:flex;align-items:center;gap:8px;margin-bottom:12px}.export-form-row label{width:70px;font-size:13px;font-weight:500;flex-shrink:0;text-align:right}.export-form-row select{flex:1;padding:6px 10px;border:1px solid var(--border);border-radius:5px;font-size:13px;background:#fff}.export-table-search-row{display:flex;align-items:center;gap:4px;margin-bottom:6px}.export-table-search{flex:1;padding:5px 10px;border:1px solid var(--border);border-radius:5px;font-size:12px;background:#fff;outline:none}.export-table-search:focus{border-color:var(--accent)}.export-table-search-clear{padding:2px 6px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:#fff;cursor:pointer;color:var(--text-muted)}.export-table-search-clear:hover{color:var(--danger);border-color:var(--danger)}.export-table-list-header{font-size:12px;padding:4px 0;color:var(--text-secondary);display:flex;align-items:center;gap:6px}.export-table-list-header input[type=checkbox]{accent-color:var(--accent)}.export-table-list{border:1px solid var(--border-light);border-radius:6px;max-height:280px;overflow-y:auto}.export-table-list-row{display:flex;align-items:center;gap:8px;padding:5px 10px;font-size:12px;border-bottom:1px solid var(--border-light)}.export-table-list-row:last-child{border-bottom:none}.export-table-list-head{background:var(--table-header-bg);font-weight:500;color:var(--text-secondary);position:sticky;top:0;z-index:1}.export-table-list-row.disabled{opacity:.4}.export-table-list-row input[type=checkbox]{accent-color:var(--accent);margin:0}.export-table-name{display:flex;align-items:center;gap:4px}.export-table-icon{font-size:14px}.export-filename-input{width:100%;padding:2px 6px;border:1px solid var(--border-light);border-radius:3px;font-size:12px;font-family:var(--font-mono);background:#fff}.export-filename-input:focus{border-color:var(--accent);outline:none}.export-loading{font-size:12px;color:var(--accent);padding:20px;text-align:center}.export-col-list{border:1px solid var(--border-light);border-radius:6px;max-height:300px;overflow-y:auto;padding:4px 0}.export-col-item{display:flex;align-items:center;gap:8px;padding:4px 14px;cursor:pointer;font-size:13px;transition:background .1s}.export-col-item:hover{background:var(--select-bg-hover)}.export-col-item input[type=checkbox]{accent-color:var(--accent);margin:0}.export-col-name{flex:1}.export-col-type{font-size:11px;color:var(--text-muted);font-family:var(--font-mono)}.export-col-key{font-size:12px}.export-col-actions{display:flex;align-items:center;gap:8px;padding:8px 0 0;font-size:12px}.export-col-actions button{padding:3px 10px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:#fff;cursor:pointer}.export-col-actions button:hover{background:var(--select-bg-hover)}.export-col-actions input[type=checkbox]{accent-color:var(--accent)}.export-options-section{margin-bottom:16px}.export-options-title{font-size:13px;font-weight:600;margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid var(--border-light)}.export-options-grid{display:flex;flex-direction:column;gap:8px}.export-options-grid input[type=checkbox]{accent-color:var(--accent)}.export-option-row{display:flex;align-items:center;gap:8px;font-size:13px}.export-option-row span:first-child{width:110px;text-align:right;color:var(--text-secondary);flex-shrink:0}.export-option-row select{padding:3px 8px;border:1px solid var(--border);border-radius:4px;font-size:12px;background:#fff}.export-stats-row{display:flex;gap:20px;margin-bottom:12px;font-size:13px}.export-stat-item{display:flex;gap:6px}.export-stat-label{font-weight:500;color:var(--text-secondary)}.export-log-box{border:1px solid var(--border-light);border-radius:6px;background:var(--bg-info);min-height:180px;max-height:280px;overflow-y:auto;padding:8px 12px;font-family:var(--font-mono);font-size:12px;margin-bottom:12px}.export-log-line{padding:2px 0;line-height:1.5}.td-container{display:flex;flex-direction:column;height:100%;background:var(--bg-main)}.td-topbar{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;border-bottom:1px solid var(--border);background:var(--bg-info);gap:12px;flex-shrink:0}.td-topbar-left{display:flex;align-items:center;gap:8px}.td-name-label{font-size:12px;color:var(--text-secondary);font-weight:500}.td-name-input{width:220px;padding:4px 8px;border:1px solid var(--border);border-radius:4px;font-size:13px;font-family:var(--font-mono);background:var(--bg-main);color:var(--text)}.td-topbar-right{display:flex;gap:6px}.td-btn{padding:4px 10px;border:1px solid var(--border);border-radius:4px;background:var(--bg-info);color:var(--text);font-size:12px;cursor:pointer;white-space:nowrap}.td-btn:hover{background:var(--select-bg-hover)}.td-btn:disabled{opacity:.4;cursor:default}.td-btn-primary{background:var(--accent);color:#fff;border-color:var(--accent)}.td-btn-primary:hover{background:var(--accent-hover)}.td-tab-bar{display:flex;border-bottom:1px solid var(--border);background:var(--bg-info);flex-shrink:0}.td-tab{padding:6px 16px;font-size:12px;font-weight:500;color:var(--text-secondary);background:none;border:none;border-bottom:2px solid transparent;cursor:pointer}.td-tab:hover{color:var(--text)}.td-tab.active{color:var(--accent);border-bottom-color:var(--accent)}.td-field-toolbar{display:flex;align-items:center;gap:4px;padding:6px 12px;border-bottom:1px solid var(--border-light);background:var(--bg-info);flex-shrink:0}.td-toolbar-sep{width:1px;height:18px;background:var(--border);margin:0 4px}.td-grid-wrapper{flex:1;overflow:auto}.td-grid{width:100%;border-collapse:collapse;font-size:12px}.td-grid thead th{position:sticky;top:0;background:var(--table-header-bg);border-bottom:1px solid var(--border);padding:5px 6px;text-align:left;font-weight:600;color:var(--text-secondary);font-size:11px;white-space:nowrap;z-index:1}.td-grid tbody tr{border-bottom:1px solid var(--border-light);cursor:pointer}.td-grid tbody tr:hover{background:var(--table-row-hover)}.td-grid tbody tr.td-row-selected{background:var(--select-bg-hover)}.td-grid td{padding:1px 2px}.td-cell-num{text-align:center;color:var(--text-muted);font-size:10px;width:30px}.td-cell-input{width:100%;padding:3px 6px;border:1px solid transparent;border-radius:3px;font-size:12px;font-family:var(--font-mono);background:transparent;color:var(--text);outline:none}.td-cell-input:focus{border-color:var(--accent);background:var(--bg-main)}.td-cell-select{width:100%;padding:3px 4px;border:1px solid transparent;border-radius:3px;font-size:12px;font-family:var(--font-mono);background:transparent;color:var(--text);outline:none;cursor:pointer}.td-cell-select:focus{border-color:var(--accent);background:var(--bg-main)}.td-cell-center{text-align:center}.td-cell-short{width:100%}.td-sql-preview{flex:1;overflow:auto;padding:12px;background:var(--bg-main)}.td-sql-preview pre{margin:0;font-family:var(--font-mono);font-size:12px;line-height:1.6;color:var(--text);white-space:pre-wrap;word-break:break-all}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display