<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title data-i18n="db_manager.title">Database Manager</title>
    <script src="/static/js/i18n.js"></script>
    <!-- Tabulator 样式 - 匹配数据库页面 -->
    <link href="/static/lib/tabulator.min.css" rel="stylesheet">
    <style>
        #resultsTable {
            width: 100%;
            min-width: 0;
        }
        .tabulator {
            background: #16213e !important;
            border: 1px solid #0f3460 !important;
            color: #e0e0e0 !important;
            font-size: 13px !important;
        }
        .tabulator-tableholder {
            background: #16213e;
        }
        .tabulator-header {
            background: #1a1a2e !important;
            border-bottom: 1px solid #0f3460 !important;
            color: #e0e0e0 !important;
        }
        .tabulator-header .tabulator-col {
            background: #1a1a2e !important;
            border-right: 1px solid #0f3460 !important;
        }
        .tabulator-header .tabulator-col:hover {
            background: #0f3460 !important;
        }
        .tabulator-row {
            background: #16213e !important;
            border-bottom: 1px solid #0f3460 !important;
            color: #e0e0e0 !important;
        }
        .tabulator-row:hover {
            background: #0f3460 !important;
        }
        .tabulator-row.tabulator-selected {
            background: #e94560 !important;
        }
        .tabulator-cell {
            border-right: 1px solid #0f3460 !important;
            padding: 6px 8px !important;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            max-width: 200px;
        }
        /* 单元格编辑样式 */
        .tabulator-cell.tabulator-editing {
            background: #1a1a2e !important;
            box-shadow: inset 0 0 0 2px #e94560 !important;
            padding: 0 !important;
        }
        .tabulator-cell.tabulator-editing input {
            width: 100%;
            height: 100%;
            background: #1a1a2e !important;
            color: #fff !important;
            border: none;
            padding: 6px 8px;
            font-size: 13px;
        }
        /* 表头固定 */
        .tabulator-header {
            position: sticky;
            top: 0;
            z-index: 10;
        }
        /* 分页固定 */
        .tabulator-footer {
            position: sticky;
            bottom: 0;
            z-index: 10;
        }
        .tabulator-footer {
            background: #1a1a2e !important;
            border-top: 1px solid #0f3460 !important;
            color: #e0e0e0 !important;
        }
        .tabulator-paginator {
            color: #e0e0e0 !important;
        }
        .tabulator-page {
            color: #e0e0e0 !important;
            background: #0f3460 !important;
            border: 1px solid #16213e !important;
        }
        .tabulator-page.active {
            background: #e94560 !important;
        }
        .tabulator-page:hover {
            background: #16213e !important;
        }
    </style>
    <script src="/static/lib/tabulator.min.js"></script>
    <script src="/static/js/clipboard.min.js"></script>
    <style>
        * { box-sizing: border-box; margin: 0; padding: 0; }
        html, body { height: 100%; overflow: auto; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #1a1a2e; color: #e0e0e0;
        }
        #app { display: flex; flex-direction: column; height: 100vh; }
        
        /* 悬浮按钮 */
        .float-btn {
            position: fixed; left: 16px; bottom: 40px; z-index: 100;
            width: 44px; height: 44px; border-radius: 12px;
            background: linear-gradient(135deg, #e94560, #ff6b6b);
            border: none; cursor: pointer;
            display: flex; align-items: center; justify-content: center;
            box-shadow: 0 4px 20px rgba(233, 69, 96, 0.4);
        }
        .float-btn:hover { transform: scale(1.05); }
        .float-btn svg { width: 22px; height: 22px; fill: #fff; }
        
        /* 抽屉侧边栏 */
        .drawer {
            position: fixed; left: -100%; top: 0; bottom: 0;
            width: 60%; max-width: 320px; z-index: 200;
            background: #16213e; border-right: 1px solid #0f3460;
            display: flex; flex-direction: column;
            transition: left 0.3s ease;
        }
        .drawer.open { left: 0; }
        .drawer-backdrop {
            position: fixed; inset: 0; z-index: 150;
            background: rgba(0,0,0,0.6);
            opacity: 0; visibility: hidden;
        }
        .drawer-backdrop.show { opacity: 1; visibility: visible; }
        .drawer-header {
            padding: 14px; border-bottom: 1px solid #0f3460;
            display: flex; align-items: center; justify-content: space-between;
        }
        .drawer-header h2 { font-size: 15px; font-weight: 500; color: #fff; }
        .drawer-header-actions { display: flex; align-items: center; gap: 6px; }
        .drawer-mini-btn {
            padding: 6px 10px; border-radius: 8px;
            background: #0f3460; border: none; color: #ccc;
            cursor: pointer; font-size: 12px;
        }
        .drawer-mini-btn:hover { color: #fff; }
        .drawer-close {
            width: 32px; height: 32px; border-radius: 8px;
            background: #0f3460; border: none; color: #888;
            cursor: pointer; font-size: 20px;
        }
        .drawer-content { flex: 1; overflow-y: auto; padding: 12px; }
        
        /* 树组件 */
        .tree { font-size: 13px; }
        .tree-node { }
        .tree-node-content {
            padding: 8px 10px; border-radius: 6px; cursor: pointer;
            display: flex; align-items: center; gap: 8px;
            transition: background 0.15s;
        }
        .tree-node-content:hover { background: #0f3460; }
        .tree-node-content.active { background: #0f3460; color: #e94560; }
        .tree-toggle {
            width: 18px; height: 18px; display: flex; align-items: center; justify-content: center;
            font-size: 10px; color: #666; flex-shrink: 0;
        }
        .tree-toggle.expanded { transform: rotate(90deg); }
        .tree-icon { width: 18px; text-align: center; flex-shrink: 0; }
        .tree-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .tree-actions { display: flex; gap: 4px; opacity: 0; transition: opacity 0.15s; }
        .tree-node-content:hover .tree-actions { opacity: 1; }
        .tree-actions button {
            padding: 2px 6px; border-radius: 4px; border: none;
            background: rgba(255,255,255,0.1); color: #888; cursor: pointer; font-size: 10px;
        }
        .tree-actions button:hover { background: #e94560; color: #fff; }
        .tree-children { padding-left: 20px; overflow: hidden; }
        .tree-children.collapsed { display: none; }
        .tree-loading { padding: 10px; text-align: center; color: #666; font-size: 12px; }
        
        /* 新建按钮 */
        .add-btn {
            width: 100%; padding: 10px; border-radius: 8px;
            background: #0f3460; border: none; color: #ccc;
            cursor: pointer; font-size: 13px; margin-bottom: 12px;
        }
        
        /* 主内容区 */
        .main-content {
            flex: 1; display: flex; flex-direction: column;
            padding: 5px 16px 16px 16px; overflow: hidden;
        }
        .top-bar {
            flex-shrink: 0; display: flex; align-items: center; gap: 8px;
            margin-bottom: 4px; flex-wrap: wrap;
        }
        .top-bar h1 { font-size: 18px; font-weight: 500; color: #fff; }
        .breadcrumb {
            display: flex; align-items: center; gap: 6px; font-size: 12px; color: #888;
            flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
        }
        .breadcrumb span { color: #e94560; }
        
        /* SQL 编辑器 */
        .sql-toggle {
            display: flex; align-items: center; justify-content: space-between;
            padding: 10px 12px; background: #0f3460; border-radius: 10px;
            cursor: pointer; margin-bottom: 2px;
        }
        .sql-toggle span { font-size: 13px; color: #ccc; }
        .sql-toggle .arrow { transition: transform 0.3s; font-size: 12px; color: #888; }
        .sql-toggle.open .arrow { transform: rotate(180deg); }
        .sql-editor {
            display: none; flex-direction: column;
            background: #16213e; border-radius: 10px; padding: 12px;
        }
        .sql-editor.open { display: flex; }
        .sql-editor-wrapper { position: relative; }
        .sql-editor textarea {
            width: 100%; height: 80px; background: #1a1a2e; border: none;
            padding: 12px; resize: none; outline: none;
            color: #fff; font-family: 'SF Mono', monospace; font-size: 12px;
            border-radius: 6px;
        }
        .sql-autocomplete {
            position: absolute; top: 100%; left: 0; right: 0;
            background: #1a1a2e; border: 1px solid #0f3460;
            border-radius: 6px; max-height: 150px; overflow-y: auto;
            z-index: 100;
        }
        .sql-autocomplete-item {
            padding: 8px 12px; cursor: pointer; color: #ccc;
            font-family: 'SF Mono', monospace; font-size: 12px;
            display: flex; align-items: center; gap: 8px;
        }
        .sql-autocomplete-item:hover, .sql-autocomplete-item.selected {
            background: #0f3460; color: #fff;
        }
        .sql-autocomplete-item .type-tag {
            font-size: 10px; padding: 2px 6px; border-radius: 4px;
            background: #2d2d2d; color: #888;
        }
        .sql-autocomplete-item .type-tag.key { background: #1e3a5f; color: #58a6ff; }
        .sql-autocomplete-item .type-tag.func { background: #3d5a3d; color: #7ee787; }
        .sql-autocomplete-item .type-tag.col { background: #5a3d2d; color: #f0883e; }
        .sql-autocomplete-item .type-tag.tbl { background: #3d3d5a; color: #a371f7; }
        .sql-autocomplete-item .type-tag.his { background: #5a3d5a; color: #db61a2; }
        .sql-toolbar { display: flex; gap: 8px; padding: 10px 0; flex-wrap: wrap; align-items: center; }
        .sql-toolbar button {
            padding: 8px 14px; border-radius: 6px; border: none;
            background: #e94560; color: #fff; cursor: pointer; font-size: 12px;
        }
        .sql-toolbar button.secondary { background: #0f3460; }
        .quick-btns { display: flex; gap: 6px; flex-wrap: wrap; margin-left: auto; }
        .quick-btns button {
            background: #0f3460; border: none; padding: 6px 10px; font-size: 11px;
            border-radius: 5px; color: #ccc; cursor: pointer;
        }
        .sql-history-list { display: flex; flex-direction: column; gap: 4px; }
        .sql-history-item {
            padding: 10px 12px; border-radius: 6px; background: #1a1a2e;
            cursor: pointer; font-size: 12px; color: #ccc;
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
        }
        .sql-history-item:hover { background: #0f3460; }
        .sql-history-item .sql-text { font-family: 'SF Mono', monospace; font-size: 11px; }
        .sql-history-item .sql-time { font-size: 10px; color: #666; margin-top: 4px; }
        .history-divider { height: 1px; background: #0f3460; margin: 12px 0; }
        .history-section-title {
            font-size: 11px; color: #888; padding: 8px 0 4px; margin-bottom: 4px;
        }
        
        /* AI SQL 生成器 */
        .ai-prompt-row {
            display: flex; gap: 8px; padding: 10px 0 0; align-items: center;
        }
        .ai-prompt-combo {
            position: relative;
            flex: 1;
            min-width: 0;
        }
        .ai-prompt-input {
            flex: 1; padding: 10px 12px; background: #1a1a2e; border: 1px solid #0f3460;
            border-radius: 6px; color: #fff; font-size: 13px; outline: none;
            transition: border-color 0.2s;
        }
        .ai-prompt-combo .ai-prompt-input {
            width: 100%;
            padding-right: 46px;
        }
        .ai-prompt-input:focus { border-color: #667eea; }
        .ai-prompt-input::placeholder { color: #666; }
        .ai-combo-toggle {
            position: absolute;
            right: 7px;
            top: 50%;
            transform: translateY(-50%);
            width: 32px;
            height: 32px;
            border-radius: 8px;
            border: 1px solid #0f3460;
            background: #0f3460;
            color: #ccc;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background 0.2s, transform 0.2s, opacity 0.2s;
        }
        .ai-combo-toggle:hover { opacity: 0.95; }
        .ai-combo-toggle:active { transform: translateY(-50%) scale(0.98); }
        .ai-combo-toggle:focus-visible { outline: 2px solid rgba(102, 126, 234, 0.9); outline-offset: 2px; }
        .btn-ai-generate {
            background: linear-gradient(135deg, #667eea, #764ba2);
            padding: 10px 20px; border-radius: 6px; border: none;
            color: #fff; cursor: pointer; font-size: 13px; font-weight: 500;
            white-space: nowrap; transition: transform 0.2s, opacity 0.2s;
        }
        .btn-ai-generate.icon {
            width: 44px;
            height: 44px;
            padding: 0;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .btn-ai-generate.icon svg { width: 18px; height: 18px; }
        .btn-ai-generate:hover { transform: scale(1.02); opacity: 0.95; }
        .btn-ai-generate:active { transform: scale(0.98); }
        .btn-ai-generate:disabled {
            opacity: 0.6; cursor: not-allowed; transform: none;
        }
        
        /* AI 历史下拉 */
        .ai-history-dropdown {
            position: absolute; left: 0; top: 100%; margin-top: 4px;
            background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px;
            width: 100%; max-height: 400px; overflow-y: auto; z-index: 100;
            display: none; box-shadow: 0 4px 20px rgba(0,0,0,0.5);
        }
        .ai-history-dropdown.show { display: block; }
        .ai-history-item {
            padding: 12px; border-bottom: 1px solid #0f3460; cursor: pointer;
        }
        .ai-history-item:hover { background: #0f3460; }
        .ai-history-item:last-child { border-bottom: none; }
        .ai-history-item .prompt {
            font-size: 12px; color: #ccc; margin-bottom: 6px;
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
        }
        .ai-history-item .sql {
            font-size: 11px; color: #888; font-family: 'SF Mono', monospace;
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
        }
        .ai-history-item .time {
            font-size: 10px; color: #666; margin-top: 4px;
        }
        .ai-loading {
            display: inline-block; width: 14px; height: 14px;
            border: 2px solid rgba(255,255,255,0.3); border-top-color: #fff;
            border-radius: 50%; animation: spin 0.8s linear infinite;
        }
        @keyframes spin { to { transform: rotate(360deg); } }
        
        /* 导出和分页 */
        .results-toolbar {
            display: flex; justify-content: space-between; align-items: center;
            padding: 8px 0; gap: 16px;
        }
        .export-btns { display: flex; gap: 6px; }
        .export-btn {
            padding: 4px 10px; font-size: 11px; border-radius: 4px;
            background: #0f3460; border: none; color: #ccc; cursor: pointer;
        }
        .pagination { display: flex; align-items: center; gap: 8px; }
        .page-btn {
            padding: 4px 8px; font-size: 11px; border-radius: 4px;
            background: #0f3460; border: none; color: #ccc; cursor: pointer;
        }
        .page-info { font-size: 11px; color: #888; min-width: 60px; text-align: center; }
        
        /* 结果区 */
        .results-tabs { display: flex; gap: 4px; padding: 8px 0; }
        .results-tab {
            padding: 6px 12px; border-radius: 6px; border: none;
            background: transparent; color: #888; cursor: pointer; font-size: 12px;
        }
        .results-tab.active { background: #0f3460; color: #e94560; }
        .results {
            flex: 1; overflow: auto; background: #16213e; border-radius: 12px;
            border: 1px solid #0f3460;
        }
        .results-empty { text-align: center; color: #666; padding: 30px; }
        .loading { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 20px; color: #666; }
        .spinner {
            width: 18px; height: 18px; border: 2px solid #0f3460;
            border-top-color: #e94560; border-radius: 50%;
            animation: spin 0.8s linear infinite;
        }
        @keyframes spin { to { transform: rotate(360deg); } }
        
        /* 数据表格 */
        .data-table-wrapper { overflow: auto; max-height: 100%; }
        .data-table { width: 100%; border-collapse: collapse; font-size: 12px; }
        .data-table th, .data-table td {
            padding: 10px 12px; text-align: left; border-bottom: 1px solid #0f3460;
            white-space: nowrap; max-width: 150px; overflow: hidden; text-overflow: ellipsis;
        }
        .data-table th {
            background: #1a1a2e; color: #e94560; font-weight: 500;
            position: sticky; top: 0; z-index: 10;
        }
        .data-table td { color: #ccc; font-family: 'SF Mono', monospace; font-size: 11px; cursor: pointer; }
        .data-table td:hover { background: #1a2a4a; }
        
        /* 状态栏 */
        .status-bar {
            flex-shrink: 0;
            display: flex; align-items: center; gap: 12px; padding: 10px 14px;
            background: #16213e; border-radius: 8px; margin-top: 10px;
            font-size: 11px; color: #888;
        }
        .status-bar .success { color: #4ade80; }
        .status-bar .error { color: #ef4444; }
        
        /* Toast 通知 */
        #toast {
            position: fixed; bottom: calc(16px + env(safe-area-inset-bottom)); top: auto; left: 50%; transform: translateX(-50%);
            background: rgba(0,0,0,0.9); color: #fff; padding: 12px 24px;
            border-radius: 8px; font-size: 14px; z-index: 9999;
            opacity: 0; transition: opacity 0.3s;
            pointer-events: none;
            max-width: calc(100vw - 24px);
            text-align: center;
        }
        #toast.show { opacity: 1; }
        #toast.error { border-left: 4px solid #ef4444; }
        #toast.success { border-left: 4px solid #4ade80; }
        
        /* 弹窗 */
        .modal {
            position: fixed; inset: 0; background: rgba(0,0,0,0.7);
            display: none; align-items: center; justify-content: center; z-index: 300;
            padding: 20px;
        }
        .modal.show { display: flex; }
        .modal-content { background: #16213e; border-radius: 12px; padding: 20px; max-width: 360px; width: 100%; }
        .modal-title { font-size: 16px; font-weight: 500; margin-bottom: 16px; }
        .form-grid { display: grid; grid-template-columns: 60px 1fr; gap: 8px 8px; }
        .form-grid .field-row { display: contents; }
        .form-grid label { font-size: 12px; color: #888; text-align: right; padding-right: 8px; line-height: 32px; }
        .form-grid .input-wrap { position: relative; }
        .form-grid input, .form-grid select { width: 100%; padding: 6px 28px 6px 8px; background: #1a1a2e; border: 1px solid #0f3460; border-radius: 4px; color: #fff; font-size: 12px; height: 32px; }
        .form-grid .copy-btn { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); background: none; border: none; color: #666; cursor: pointer; font-size: 11px; }
        .form-grid .copy-btn:hover { color: #fff; }
        .form-grid .full-width { grid-column: 1 / -1; }
        .modal-actions { display: flex; gap: 12px; margin-top: 20px; }
        .modal-actions button {
            flex: 1; padding: 12px; border-radius: 8px; border: none;
            cursor: pointer; font-size: 14px;
        }
        .modal-actions .cancel { background: #0f3460; color: #ccc; }
        .modal-actions .confirm { background: #e94560; color: #fff; }
    </style>
</head>
<body>
    <div id="app">
        <!-- 悬浮按钮 -->
        <button class="float-btn" onclick="toggleDrawer()">
            <svg viewBox="0 0 24 24"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
        </button>
        
        <!-- 抽屉遮罩 -->
        <div class="drawer-backdrop" id="drawerBackdrop" onclick="closeDrawer()"></div>
        
        <!-- 抽屉 -->
        <div class="drawer" id="drawer">
            <div class="drawer-header">
                <h2 data-i18n="db_manager.heading">Database Manager</h2>
                <div class="drawer-header-actions">
                    <input id="dbConnImportFile" type="file" accept="application/json" style="display:none;" onchange="onImportDbConnectionsFileChange(event)">
                    <button class="drawer-mini-btn" onclick="triggerImportDbConnections()" data-i18n="db_manager.import">Import</button>
                    <button class="drawer-mini-btn" onclick="exportDbConnections()" data-i18n="db_manager.export">Export</button>
                    <button class="drawer-close" onclick="closeDrawer()" aria-label="Close" data-i18n-aria-label="common.close">×</button>
                </div>
            </div>
            <div class="drawer-content">
                <button class="add-btn" onclick="openAddModal()" data-i18n="db_manager.new_connection">+ New Connection</button>
                <div class="tree" id="dbTree"></div>
            </div>
        </div>
        
        <!-- 主内容区 -->
        <div class="main-content">
            <div class="top-bar">
                <h1 data-i18n="db_manager.heading">Database Manager</h1>
                <div class="breadcrumb" id="breadcrumb"><span data-i18n="db_manager.no_selection">Not selected</span></div>
            </div>
            
            <!-- SQL 编辑器 -->
            <div class="sql-toggle" id="sqlToggle" onclick="toggleSqlEditor()">
                <span data-i18n="db_manager.sql_query">📝 SQL Query</span>
                <span class="arrow">▼</span>
            </div>
            <div class="sql-editor" id="sqlEditorPanel">
                <div class="sql-editor-wrapper">
                    <textarea id="sqlEditor" placeholder="Enter SQL... (Ctrl+Enter to execute)" data-i18n-placeholder="db_manager.sql_placeholder"></textarea>
                    <div class="sql-autocomplete" id="sqlAutocomplete" style="display:none;"></div>
                </div>
                <div class="sql-toolbar">
                    <button onclick="executeQuery()" title="Execute (Ctrl+Enter)" data-i18n-title="db_manager.execute_hotkey">▶</button>
                    <button class="secondary" onclick="clearEditor()" title="Clear" data-i18n-title="db_manager.clear">🗑️</button>
                    <button class="secondary" onclick="openSqlHistory()" title="History" data-i18n-title="db_manager.history">📜</button>
                    <button class="secondary" onclick="sqlHistoryPrev()" title="Previous" data-i18n-title="db_manager.prev">⏮</button>
                    <button class="secondary" onclick="sqlHistoryNext()" title="Next" data-i18n-title="db_manager.next">⏭</button>
                </div>
                <div class="ai-prompt-row">
                    <div class="ai-prompt-combo" id="aiPromptCombo">
                        <input type="text" id="aiPromptInput" class="ai-prompt-input"
                               placeholder="✨ Describe what you want, AI will generate/modify SQL..." data-i18n-placeholder="db_manager.ai_prompt_placeholder">
                        <button id="btnAiHistory" type="button" class="ai-combo-toggle" onclick="toggleAiHistory()" aria-label="History" data-i18n-aria-label="db_manager.ai_history">▾</button>
                        <div class="ai-history-dropdown" id="aiHistoryDropdown"></div>
                    </div>
                    <button id="btnAiGenerate" class="btn-ai-generate icon" onclick="generateSqlFromPrompt()" title="Generate SQL with AI" aria-label="Generate SQL with AI" data-i18n-title="db_manager.ai_generate" data-i18n-aria-label="db_manager.ai_generate">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                            <path d="M12 2l1.6 5.4L19 9l-5.4 1.6L12 16l-1.6-5.4L5 9l5.4-1.6L12 2z"></path>
                            <path d="M19 13l.8 2.7 2.2.7-2.2.7L19 20l-.8-2.6-2.2-.7 2.2-.7L19 13z"></path>
                        </svg>
                    </button>
                </div>
            </div>
            
            <!-- SQL 历史侧边栏 -->
            <div class="drawer-backdrop" id="historyBackdrop" onclick="closeSqlHistory()"></div>
            <div class="drawer" id="historyDrawer" style="width:50%;max-width:280px;">
                <div class="drawer-header">
                    <h2 data-i18n="db_manager.sql_history">📜 SQL History</h2>
                    <button class="drawer-close" onclick="closeSqlHistory()" aria-label="Close" data-i18n-aria-label="common.close">×</button>
                </div>
                <div class="drawer-content">
                    <div style="font-size:11px;color:#888;margin-bottom:8px;" data-i18n="db_manager.favorites">Favorites</div>
                    <div class="sql-history-list" id="sqlHistoryList"></div>
                </div>
            </div>
            
            <!-- 工具栏 -->
            <div class="results-toolbar" id="resultsToolbar" style="display:none;">
                <div class="export-btns">
                    <button class="export-btn" onclick="exportData('csv')">CSV</button>
                    <button class="export-btn" onclick="exportData('xls')">XLS</button>
                    <button class="export-btn" onclick="exportData('sql')">SQL</button>
                </div>
                <div class="pagination" id="paginationControls">
                    <button class="page-btn" onclick="goToPage(1)">«</button>
                    <button class="page-btn" onclick="goToPage(currentPage-1)">‹</button>
                    <span class="page-info" id="pageInfo">1 / 1</span>
                    <button class="page-btn" onclick="goToPage(currentPage+1)">›</button>
                    <button class="page-btn" onclick="goToPage(totalPages)">»</button>
                </div>
            </div>
            
            <!-- 结果 -->
            <div class="results-tabs">
                <button class="results-tab active" id="tabResults" onclick="switchResultsTab('results')" data-i18n="db_manager.data">Data</button>
                <button class="results-tab" id="tabSchema" onclick="switchResultsTab('schema')" data-i18n="db_manager.table_structure">Structure</button>
            </div>
            <div class="results" id="resultsContent">
                <div class="results-empty" data-i18n="db_manager.results_empty">Select a table to auto query</div>
            </div>
            <div class="results" id="schemaPanel" style="display:none;">
                <div class="results-empty" data-i18n="db_manager.schema_empty">Select a table to view schema</div>
            </div>
            
            <!-- 状态栏 -->
            <div id="toast"></div>
        <div class="status-bar" id="statusBar"><span data-i18n="db_manager.status_ready">Ready</span></div>
        </div>
    </div>
    
    <!-- 添加连接弹窗 -->
    <div id="addModal" class="modal">
        <div class="modal-content" style="max-width:420px;">
            <div class="modal-title" data-i18n="db_manager.add_connection_title">New Connection</div>
            <div class="form-grid stacked">
                <div class="field-row"><label data-i18n="db_manager.engine">Engine</label><select id="dbEngine"><option value="mysql">MySQL</option><option value="postgresql">PostgreSQL</option><option value="sqlite">SQLite</option><option value="mariadb">MariaDB</option><option value="oracle">Oracle</option><option value="mssql">SQL Server</option></select></div>
                <div class="field-row"><label data-i18n="common.name">Name</label><div class="input-wrap"><input type="text" id="dbName" placeholder="e.g., Production DB" data-i18n-placeholder="db_manager.name_placeholder"><button class="copy-btn" onclick="copyToClipboard($$('dbName').value)">📋</button></div></div>
                <div class="field-row full-width">
                    <label data-i18n="db_manager.url">URL</label>
                    <div class="input-wrap">
                        <input type="text" id="dbUrl" placeholder="mysql+pymysql://user:pass@host:3306/db">
                        <button class="copy-btn" onclick="applyDbUrl(true)" style="right:60px;">⚡</button>
                        <button class="copy-btn" onclick="copyToClipboard($$('dbUrl').value)">📋</button>
                    </div>
                </div>
                <div class="field-row"><label data-i18n="db_manager.host">Host</label><div class="input-wrap"><input type="text" id="dbHost" placeholder="localhost"><button class="copy-btn" onclick="copyToClipboard($$('dbHost').value)">📋</button></div></div>
                <div class="field-row"><label data-i18n="db_manager.port">Port</label><div class="input-wrap"><input type="number" id="dbPort" value="3306"><button class="copy-btn" onclick="copyToClipboard($$('dbPort').value)">📋</button></div></div>
                <div class="field-row"><label data-i18n="db_manager.username">Username</label><div class="input-wrap"><input type="text" id="dbUser" placeholder="root"><button class="copy-btn" onclick="copyToClipboard($$('dbUser').value)">📋</button></div></div>
                <div class="field-row"><label data-i18n="db_manager.password">Password</label><div class="input-wrap"><input type="password" id="dbPass" placeholder="Password" data-i18n-placeholder="db_manager.password_placeholder"><button class="copy-btn" onclick="copyToClipboard($$('dbPass').value)">📋</button></div></div>
                <div class="field-row"><label data-i18n="db_manager.database">Database</label><div class="input-wrap"><input type="text" id="dbDatabase" placeholder="Database name" data-i18n-placeholder="db_manager.database_placeholder"><button class="copy-btn" onclick="copyToClipboard($$('dbDatabase').value)">📋</button></div></div>
                <div class="field-row sqlite-field" style="display:none;"><label data-i18n="db_manager.file_path">File Path</label><div class="input-wrap"><input type="text" id="dbSqlitePath" placeholder="/path/to/db.sqlite"><button class="copy-btn" onclick="copyToClipboard($$('dbSqlitePath').value)">📋</button></div></div>
            </div>
            <div class="modal-actions">
                <button class="cancel" onclick="closeAddModal()" data-i18n="common.cancel">Cancel</button>
                <button class="secondary" onclick="testConnection()" style="background:#0f3460;" data-i18n="common.test">Test</button>
                <button class="confirm" onclick="testAndSave()" data-i18n="common.save">Save</button>
            </div>
        </div>
    </div>
    
    <!-- 编辑连接弹窗 -->
    <div id="editModal" class="modal">
        <div class="modal-content" style="max-width:420px;">
            <div class="modal-title" data-i18n="db_manager.edit_connection_title">Edit Connection</div>
            <input type="hidden" id="editConnId">
            <div class="form-grid stacked">
                <div class="form-group full-width url-field">
                    <label data-i18n="db_manager.url">URL</label>
                    <div class="input-wrap">
                        <input type="text" id="editDbUrl" readonly style="background:#0f3460;color:#4ade80;font-size:11px;">
                        <button class="copy-btn" onclick="copyToClipboard($$('editDbUrl').value)">📋</button>
                    </div>
                </div>
                <div class="field-row"><label data-i18n="common.name">Name</label><div class="input-wrap"><input type="text" id="editDbName"><button class="copy-btn" onclick="copyToClipboard($$('editDbName').value)">📋</button></div></div>
                <div class="field-row"><label data-i18n="db_manager.host">Host</label><div class="input-wrap"><input type="text" id="editDbHost"><button class="copy-btn" onclick="copyToClipboard($$('editDbHost').value)">📋</button></div></div>
                <div class="field-row"><label data-i18n="db_manager.port">Port</label><div class="input-wrap"><input type="number" id="editDbPort"><button class="copy-btn" onclick="copyToClipboard($$('editDbPort').value)">📋</button></div></div>
                <div class="field-row"><label data-i18n="db_manager.username">Username</label><div class="input-wrap"><input type="text" id="editDbUser"><button class="copy-btn" onclick="copyToClipboard($$('editDbUser').value)">📋</button></div></div>
                <div class="field-row"><label data-i18n="db_manager.password">Password</label><div class="input-wrap"><input type="password" id="editDbPass" placeholder="Leave blank to keep current password" data-i18n-placeholder="db_manager.keep_password_placeholder"><button class="copy-btn" onclick="copyToClipboard($$('editDbPass').value)">📋</button></div></div>
                <div class="field-row"><label data-i18n="db_manager.database">Database</label><div class="input-wrap"><input type="text" id="editDbDatabase"><button class="copy-btn" onclick="copyToClipboard($$('editDbDatabase').value)">📋</button></div></div>
                <div class="field-row" style="display:none;"><label data-i18n="db_manager.file_path">File Path</label><div class="input-wrap"><input type="text" id="editDbSqlitePath"><button class="copy-btn" onclick="copyToClipboard($$('editDbSqlitePath').value)">📋</button></div></div>
            </div>
            <div class="modal-actions">
                <button class="cancel" onclick="closeEditModal()" data-i18n="common.cancel">Cancel</button>
                <button class="secondary" onclick="testEditConnection()" style="background:#0f3460;" data-i18n="common.test">Test</button>
                <button class="confirm" onclick="saveEditConnection()" data-i18n="common.save">Save</button>
            </div>
        </div>
    </div>
    
    <!-- Confirm 弹窗 -->
    <div id="confirmBackdrop" class="modal" onclick="closeConfirmModal()" style="display:none;background:rgba(0,0,0,0.5);">
        <div class="modal-content" onclick="event.stopPropagation()" style="max-width:320px;">
            <div class="modal-title" id="confirmTitle" data-i18n="common.confirm">Confirm</div>
            <div id="confirmMessage" style="color:#888;margin-bottom:20px;"></div>
            <div style="display:flex;gap:12px;justify-content:flex-end;">
                <button onclick="closeConfirmModal()" style="padding:10px 16px;background:#0f3460;border:none;border-radius:8px;color:#ccc;cursor:pointer;" data-i18n="common.cancel">Cancel</button>
                <button id="confirmBtn" onclick="performConfirm()" style="padding:10px 16px;border:none;border-radius:8px;color:#fff;cursor:pointer;" data-i18n="common.confirm">Confirm</button>
            </div>
        </div>
    </div>
    
    <script>
        const $$ = id => document.getElementById(id);
        let connections = [], currentConn = null, currentDb = null, currentTable = null;
        let allResults = [], currentPage = 1, totalPages = 1, pageSize = 200;
        let __confirmCallback = null;
        let currentResultsTab = 'results';
        let __dbUiState = null;
        let __dbUiStateSaveTimer = null;
        
        const ENGINE_CONFIG = {
            mysql: { port: 3306, icon: '🐬' }, postgresql: { port: 5432, icon: '🐘' },
            sqlite: { port: 0, icon: '📄' }, mariadb: { port: 3306, icon: '🐬' },
            oracle: { port: 1521, icon: '🔴' }, mssql: { port: 1433, icon: '🪟' }
        };

        function __t(key, fallback) {
            try {
                if (typeof I18n !== 'undefined' && I18n && typeof I18n.t === 'function') {
                    const v = I18n.t(key);
                    if (v && v !== key) return v;
                }
            } catch (e) {}
            return fallback;
        }

        async function loadDbUiState() {
            try {
                const r = await fetch('/api/db/ui-state');
                const data = await r.json();
                if (data && data.success) __dbUiState = data.data || {};
                else __dbUiState = {};
            } catch (e) {
                __dbUiState = {};
            }
        }

        function __mergeDbUiState(partial) {
            if (!__dbUiState || typeof __dbUiState !== 'object') __dbUiState = {};
            if (!partial || typeof partial !== 'object') return __dbUiState;
            Object.keys(partial).forEach(function(k) {
                __dbUiState[k] = partial[k];
            });
            return __dbUiState;
        }

        function saveDbUiState(partial, immediate) {
            const next = __mergeDbUiState(partial);
            if (__dbUiStateSaveTimer) {
                clearTimeout(__dbUiStateSaveTimer);
                __dbUiStateSaveTimer = null;
            }
            const doSave = function() {
                const payload = Object.assign({}, next);
                fetch('/api/db/ui-state', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(payload),
                    keepalive: true
                }).catch(function() {});
            };
            if (immediate) doSave();
            else __dbUiStateSaveTimer = setTimeout(doSave, 400);
        }

        function __setSqlEditorOpen(open) {
            const panel = $$('sqlEditorPanel');
            const toggle = $$('sqlToggle');
            if (!panel || !toggle) return;
            const want = !!open;
            panel.classList.toggle('open', want);
            toggle.classList.toggle('open', want);
        }

        function __expandTreeTo(nodeEl) {
            let el = nodeEl;
            while (el) {
                if (el.classList && el.classList.contains('tree-children')) {
                    el.classList.remove('collapsed');
                    const parentNode = el.parentElement;
                    const toggle = parentNode && parentNode.querySelector ? parentNode.querySelector('.tree-toggle') : null;
                    if (toggle) toggle.classList.add('expanded');
                }
                el = el.parentElement;
            }
        }

        function __activateTreeNode(type, connId, id) {
            const nodes = Array.from(document.querySelectorAll('.tree-node'));
            let found = null;
            for (let i = 0; i < nodes.length; i++) {
                const n = nodes[i];
                if (!n || n.dataset.type !== type) continue;
                if (typeof connId === 'string' && connId && n.dataset.connid !== connId) continue;
                if (typeof id === 'string' && id && n.dataset.id !== id) continue;
                found = n;
                break;
            }
            if (!found) return false;
            __expandTreeTo(found.parentElement);
            document.querySelectorAll('.tree-node-content').forEach(n => n.classList.remove('active'));
            const content = found.querySelector('.tree-node-content');
            if (content) content.classList.add('active');
            return true;
        }

        function applyDbUiState() {
            const st = __dbUiState || {};
            const connId = typeof st.conn_id === 'string' ? st.conn_id : '';
            const db = typeof st.db === 'string' ? st.db : '';
            const table = typeof st.table === 'string' ? st.table : '';
            const sql = typeof st.sql === 'string' ? st.sql : '';
            const aiPrompt = typeof st.ai_prompt === 'string' ? st.ai_prompt : '';
            const tab = typeof st.tab === 'string' ? st.tab : '';

            if (sql && $$('sqlEditor')) $$('sqlEditor').value = sql;
            if (aiPrompt && $$('aiPromptInput')) $$('aiPromptInput').value = aiPrompt;
            if (typeof st.sql_open === 'boolean') __setSqlEditorOpen(st.sql_open);
            if (tab === 'results' || tab === 'schema') switchResultsTab(tab);

            if (connId) {
                const conn = connections.find(c => c.id === connId);
                if (conn) currentConn = conn;
                currentDb = db || null;
                currentTable = table || null;
                updateBreadcrumb();

                if (table) __activateTreeNode('table', connId, table);
                else if (db) __activateTreeNode('db', connId, db);
                else __activateTreeNode('conn', connId, connId);

                if (currentConn && sql) executeQuery();
            }
        }
        
        // 加载树数据
        async function loadTree() {
            $$('dbTree').innerHTML = '<div class="tree-loading"><div class="spinner"></div>' + __t('common.loading', 'Loading...') + '</div>';
            try {
                const r = await fetch('/api/db/tree');
                const data = await r.json();
                if (data.success) {
                    renderTree(data.data || []);
                } else {
                    $$('dbTree').innerHTML = '<div class="results-empty">' + __t('db_manager.load_failed', 'Load failed') + '</div>';
                }
            } catch (e) {
                $$('dbTree').innerHTML = '<div class="results-empty">' + __t('db_manager.load_failed', 'Load failed') + '</div>';
            }
        }
        
        // 渲染树
        function renderTree(treeData) {
            window.dbTreeData = treeData; // 保存供自动补全使用
            updateAutocompleteData(); // 更新表列表
            let html = '<div class="tree">';
            treeData.forEach(conn => {
                html += renderTreeNode(conn, 'conn', null);
            });
            html += '</div>';
            $$('dbTree').innerHTML = html || '<div class="results-empty">' + __t('db_manager.no_connections', 'No connections') + '</div>';
        }
        
        function renderTreeNode(node, type, parentConnId) {
            const id = type === 'conn' ? node.id : node.name;
            const connId = type === 'conn' ? node.id : parentConnId;
            const label = type === 'conn' ? node.name : node.name;
            const icon = type === 'conn' ? (ENGINE_CONFIG[node.engine || 'mysql']?.icon || '🗄️') : (type === 'db' ? '📁' : '📋');
            const hasChildren = node.children && node.children.length > 0 && type !== 'table';
            const expanded = type === 'conn';
            const isToggleable = hasChildren;
            
            let html = `<div class="tree-node" data-type="${type}" data-id="${id}" data-connid="${connId}" data-db="${type === 'conn' ? node.database : (type === 'db' ? node.name : parentConnId)}">
                <div class="tree-node-content" onclick="onNodeClick(this, '${type}', '${id}', '${type === 'db' ? node.name : (type === 'table' ? parentConnId : node.database)}')">
                    ${type === 'table' ? '' : `<span class="tree-toggle ${expanded ? 'expanded' : ''}" onclick="event.stopPropagation();toggleNode(this)">▶</span>`}
                    <span class="tree-icon">${icon}</span>
                    <span class="tree-label">${label}</span>
                    ${type === 'conn' ? `<div class="tree-actions">
                        <button onclick="event.stopPropagation();openEditModal('${id}')">${__t('common.edit', 'Edit')}</button>
                        <button onclick="event.stopPropagation();deleteConnection('${id}')">${__t('common.delete', 'Delete')}</button>
                    </div>` : ''}
                </div>`;
            
            if (hasChildren) {
                html += `<div class="tree-children ${expanded ? '' : 'collapsed'}">`;
                node.children.forEach(child => {
                    const childType = type === 'conn' ? 'db' : (child.children && child.children.length > 0 ? 'db' : 'table');
                    html += renderTreeNode(child, childType, connId);
                });
                html += '</div>';
            }
            html += '</div>';
            return html;
        }
        
        function toggleNode(el) {
            el.classList.toggle('expanded');
            const children = el.parentElement.nextElementSibling;
            if (children && children.classList.contains('tree-children')) {
                children.classList.toggle('collapsed');
            }
        }
        
        async function onNodeClick(el, type, id, dbName) {
            // 点击可展开的节点时，toggle展开/折叠
            const nodeEl = el.closest('.tree-node');
            const hasChildren = nodeEl.querySelector('.tree-children');
            if (hasChildren && type !== 'table') {
                const toggle = nodeEl.querySelector('.tree-toggle');
                if (toggle) {
                    toggleNode(toggle);
                }
            }
            
            // 更新选中状态
            document.querySelectorAll('.tree-node-content').forEach(n => n.classList.remove('active'));
            el.classList.add('active');
            
            const connId = nodeEl.dataset.connid;
            const conn = connections.find(c => c.id === connId);
            if (!conn) { console.error('Connection not found:', connId); return; }

            if (type === 'conn') {
                currentConn = conn;
                currentDb = null;
                currentTable = null;
                updateBreadcrumb();
                saveDbUiState({ conn_id: connId, db: '', table: '' });
            } else if (type === 'db') {
                currentConn = conn;
                currentDb = id;
                currentTable = null;
                updateBreadcrumb();
                saveDbUiState({ conn_id: connId, db: String(id || ''), table: '' });
            } else if (type === 'table') {
                currentConn = conn;
                currentDb = dbName;
                currentTable = id;
                updateBreadcrumb();
                updateAutocompleteData();
                saveDbUiState({ conn_id: connId, db: String(dbName || ''), table: String(id || '') });

                closeDrawer();

                const sql = `SELECT * FROM \`${id}\` LIMIT 200`;
                $$('sqlEditor').value = sql;
                saveDbUiState({ sql: sql }, false);
                executeQuery();
            }
        }
        
        function updateBreadcrumb() {
            let html = '';
            if (currentConn) html += `<span>${currentConn.name}</span>`;
            if (currentDb) html += ` / <span>${currentDb}</span>`;
            if (currentTable) html += ` / <span>${currentTable}</span>`;
            $$('breadcrumb').innerHTML = html || '<span>' + __t('db_manager.no_selection', 'Not selected') + '</span>';
        }
        
        window.toggleDrawer = function() { $$('drawer').classList.toggle('open'); $$('drawerBackdrop').classList.toggle('show'); };
        window.closeDrawer = function() { $$('drawer').classList.remove('open'); $$('drawerBackdrop').classList.remove('show'); };
        
        // SQL历史
        const FIXED_SQLS = [
            { sql: 'SHOW TABLES', label: 'Tables' },
            { sql: 'SHOW PROCESSLIST', label: 'Process' },
            { sql: 'DESCRIBE `table_name`', label: 'Desc' },
            { sql: 'SELECT COUNT(*) FROM `table_name`', label: 'Count' },
            { sql: 'SHOW STATUS', label: 'Status' }
        ];
        
        function openSqlHistory() {
            renderSqlHistory();
            $$('historyDrawer').classList.add('open');
            $$('historyBackdrop').classList.add('show');
        }
        
        function closeSqlHistory() {
            $$('historyDrawer').classList.remove('open');
            $$('historyBackdrop').classList.remove('show');
        }
        
        function renderSqlHistory() {
            const fixedHtml = FIXED_SQLS.map(s => 
                `<div class="sql-history-item" onclick="useHistorySql('${s.sql.replace(/`/g, '\\`')}')">
                    <div class="sql-text">${s.sql}</div>
                </div>`
            ).join('');
            
            const rawHistory = localStorage.getItem('db_sql_history') || '[]';
            let history = [];
            try {
                history = JSON.parse(rawHistory);
            } catch (e) {}
            
            const historyHtml = history.length ? history.map(s => {
                if (!s || !s.sql) return '';
                return `<div class="sql-history-item" onclick="useHistorySql('${s.sql.replace(/'/g, "\\'").replace(/\n/g, ' ')}')">
                    <div class="sql-text">${s.sql.length > 50 ? s.sql.substring(0, 50) + '...' : s.sql}</div>
                    <div class="sql-time">${new Date(s.time).toLocaleString()}</div>
                </div>`;
            }).join('') : '<div style="padding:20px;text-align:center;color:#666;font-size:12px;">' + __t('db_manager.no_history', 'No history') + '</div>';
            
            $$('sqlHistoryList').innerHTML = `
                <div class="history-section-title">${__t('db_manager.common_sql', 'Common SQL')}</div>
                ${fixedHtml}
                ${history.length ? '<div class="history-divider"></div><div class="history-section-title">' + __t('db_manager.history_records', 'History') + '</div>' : ''}
                ${historyHtml}
            `;
        }
        
        function useHistorySql(sql) {
            $$('sqlEditor').value = sql;
            closeSqlHistory();
            toggleSqlEditor();
            saveDbUiState({ sql: String(sql || ''), sql_open: $$('sqlEditorPanel').classList.contains('open') });
            executeQuery();
        }

        let __sqlHistoryNavIndex = -1;

        function __getSqlHistoryNavList() {
            const rawHistory = localStorage.getItem('db_sql_history') || '[]';
            let history = [];
            try {
                history = JSON.parse(rawHistory);
            } catch (e) {
                history = [];
            }
            return (Array.isArray(history) ? history : [])
                .map(h => (h && h.sql) || '')
                .filter(s => typeof s === 'string' && s.trim());
        }

        function __ensureSqlEditorOpen() {
            const panel = $$('sqlEditorPanel');
            if (panel && !panel.classList.contains('open')) toggleSqlEditor();
        }

        function __setSqlEditorValue(sql) {
            __ensureSqlEditorOpen();
            const ta = $$('sqlEditor');
            if (!ta) return;
            ta.value = sql || '';
            saveDbUiState({ sql: String(ta.value || ''), sql_open: $$('sqlEditorPanel').classList.contains('open') });
            try {
                ta.selectionStart = ta.selectionEnd = ta.value.length;
            } catch (e) {}
            if (typeof closeAutocomplete === 'function') closeAutocomplete();
            ta.focus();
        }

        function sqlHistoryPrev() {
            const list = __getSqlHistoryNavList();
            if (!list.length) {
                showStatus(__t('db_manager.no_history', 'No history'), true);
                return;
            }

            const current = String($$('sqlEditor')?.value || '').trim();
            const found = current ? list.indexOf(current) : -1;
            if (found >= 0) __sqlHistoryNavIndex = found;

            if (__sqlHistoryNavIndex < 0) __sqlHistoryNavIndex = 0;
            else if (__sqlHistoryNavIndex < list.length - 1) __sqlHistoryNavIndex += 1;

            __setSqlEditorValue(list[__sqlHistoryNavIndex] || '');
        }

        function sqlHistoryNext() {
            const list = __getSqlHistoryNavList();
            if (!list.length) {
                showStatus(__t('db_manager.no_history', 'No history'), true);
                return;
            }

            const current = String($$('sqlEditor')?.value || '').trim();
            const found = current ? list.indexOf(current) : -1;
            if (found >= 0) __sqlHistoryNavIndex = found;

            if (__sqlHistoryNavIndex < 0) __sqlHistoryNavIndex = 0;
            else if (__sqlHistoryNavIndex > 0) __sqlHistoryNavIndex -= 1;

            __setSqlEditorValue(list[__sqlHistoryNavIndex] || '');
        }
        
        function toggleSqlEditor() {
            $$('sqlEditorPanel').classList.toggle('open');
            $$('sqlToggle').classList.toggle('open');
            saveDbUiState({ sql_open: $$('sqlEditorPanel').classList.contains('open') });
        }
        function clearEditor() {
            $$('sqlEditor').value = '';
            closeAutocomplete();
            saveDbUiState({ sql: '' });
        }
        
        // ========== SQL 自动补全 ==========
        const SQL_KEYWORDS = [
            'SELECT', 'FROM', 'WHERE', 'AND', 'OR', 'NOT', 'IN', 'LIKE', 'BETWEEN',
            'JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'INNER JOIN', 'OUTER JOIN', 'ON',
            'ORDER BY', 'ASC', 'DESC', 'LIMIT', 'OFFSET',
            'GROUP BY', 'HAVING', 'DISTINCT', 'COUNT', 'SUM', 'AVG', 'MIN', 'MAX',
            'INSERT INTO', 'VALUES', 'UPDATE', 'SET', 'DELETE FROM',
            'CREATE TABLE', 'DROP TABLE', 'ALTER TABLE', 'ADD COLUMN', 'MODIFY COLUMN',
            'INDEX', 'UNIQUE', 'PRIMARY KEY', 'FOREIGN KEY',
            'AS', 'NULL', 'IS NULL', 'IS NOT NULL', 'EXISTS', 'ALL', 'ANY',
            'UNION', 'UNION ALL', 'INTERSECT', 'EXCEPT',
            'CASE', 'WHEN', 'THEN', 'ELSE', 'END', 'AS', 'CAST', 'COALESCE', 'IFNULL', 'NULLIF'
        ];
        const SQL_FUNCTIONS = [
            'NOW()', 'CURDATE()', 'CURTIME()', 'DATE()', 'TIME()', 'YEAR()', 'MONTH()', 'DAY()', 'HOUR()', 'MINUTE()', 'SECOND()',
            'DATE_FORMAT()', 'DATE_ADD()', 'DATE_SUB()', 'DATEDIFF()', 'TIMESTAMPDIFF()',
            'CONCAT()', 'CONCAT_WS()', 'SUBSTRING()', 'LEFT()', 'RIGHT()', 'LENGTH()', 'TRIM()', 'UPPER()', 'LOWER()',
            'REPLACE()', 'INSERT()', 'INSTR()', 'LOCATE()',
            'ROUND()', 'FLOOR()', 'CEIL()', 'ABS()', 'SQRT()', 'POW()', 'MOD()',
            'IFNULL()', 'COALESCE()', 'NULLIF()', 'IF()', 'CASE WHEN()',
            'RAND()', 'UUID()', 'LAST_INSERT_ID()', 'ROW_COUNT()',
            'COUNT()', 'SUM()', 'AVG()', 'MIN()', 'MAX()', 'GROUP_CONCAT()'
        ];
        let _acTableNames = [], _acColumns = {};
        let _acHistory = JSON.parse(localStorage.getItem('db_sql_history') || '[]').map(h => (h && h.sql) || h).filter(h => h && typeof h === 'string').filter((v, i, a) => a.indexOf(v) === i);
        let _acSelected = -1, _acItems = [];
        let _acReplaceStart = 0, _acReplaceEnd = 0, _acReqSeq = 0;
        
        function updateAutocompleteData() {
            // 更新表名列表
            if (window.dbTreeData) {
                _acTableNames = [];
                const findTables = nodes => {
                    nodes.children?.forEach(c => {
                        if (c.children) findTables(c);
                        else _acTableNames.push(c.name);
                    });
                };
                dbTreeData.forEach(findTables);
            }
        }
        
        async function loadTableColumns(tableName) {
            if (!currentConn || !tableName) return Promise.resolve([]);
            if (_acColumns[tableName]) return Promise.resolve(_acColumns[tableName]);
            try {
                const r = await fetch(`/api/db/${currentConn.id}/table/${encodeURIComponent(tableName)}/columns`);
                const d = await r.json();
                _acColumns[tableName] = d.data?.columns || [];
                return Promise.resolve(_acColumns[tableName]);
            } catch(e) { _acColumns[tableName] = []; return Promise.resolve([]); }
        }

        function getLastPartTableName(raw) {
            var s = String(raw || '').trim();
            if (!s) return '';
            s = s.replace(/`/g, '');
            var parts = s.split('.');
            return parts[parts.length - 1] || '';
        }

        function extractTableRefs(sqlBeforeCursor) {
            var text = String(sqlBeforeCursor || '');
            var refs = [];
            var re = /\b(from|join)\s+`?([\w.]+)`?(?:\s+(?:as\s+)?`?(\w+)`?)?/ig;
            var m;
            while ((m = re.exec(text))) {
                var tableRaw = m[2] || '';
                var alias = m[3] || '';
                var tableName = getLastPartTableName(tableRaw);
                if (!tableName) continue;
                refs.push({ table: tableName, alias: alias });
            }
            return refs;
        }

        function getAliasMap(sqlBeforeCursor) {
            var refs = extractTableRefs(sqlBeforeCursor);
            var map = {};
            refs.forEach(function(r) {
                map[r.table] = r.table;
                if (r.alias) map[r.alias] = r.table;
            });
            return map;
        }

        function getPreferredTable(sqlBeforeCursor) {
            var refs = extractTableRefs(sqlBeforeCursor);
            if (!refs.length) return '';
            return refs[refs.length - 1].table || '';
        }

        function __isWordChar(ch) {
            return !!ch && /[A-Za-z0-9_`]/.test(ch);
        }

        function __makeItem(label, type, insertText) {
            return { label: label, type: type, insertText: (typeof insertText === 'string' ? insertText : label) };
        }

        function __contextWantsTables(beforeNoToken) {
            return /\b(from|join)\s*$/i.test(String(beforeNoToken || ''));
        }

        function __contextWantsColumns(beforeNoToken) {
            const s = String(beforeNoToken || '');
            if (/\b(select|where|on|and|or|having)\s*$/i.test(s)) return true;
            if (/\b(order\s+by|group\s+by)\s*$/i.test(s)) return true;
            if (/[,(]\s*$/.test(s)) return true;
            return false;
        }

        function __dedupeItems(items) {
            const seen = new Set();
            const out = [];
            for (let i = 0; i < items.length; i++) {
                const it = items[i];
                const key = (it && (it.insertText || it.label)) || '';
                if (!key || seen.has(key)) continue;
                seen.add(key);
                out.push(it);
            }
            return out;
        }
        
        function showAutocomplete(text, cursorPos) {
            const reqId = ++_acReqSeq;
            const before = text.substring(0, cursorPos);
            const match = before.match(/[\w`*.]+$/);
            const rawToken = match ? String(match[0] || '').replace(/`/g, '') : '';
            const tokenUpper = rawToken.toUpperCase();
            const tokenStart = match ? (cursorPos - match[0].length) : cursorPos;
            const beforeNoToken = before.substring(0, tokenStart);
            const aliasMap = getAliasMap(before);
            const endsWithDot = rawToken.endsWith('.');
            const isQualified = rawToken.indexOf('.') >= 0;
            const wantsTables = __contextWantsTables(beforeNoToken);
            const wantsColumns = __contextWantsColumns(beforeNoToken);
            
            _acReplaceStart = tokenStart;
            _acReplaceEnd = cursorPos;

            const items = [];

            if (isQualified) {
                const lastDot = rawToken.lastIndexOf('.');
                const qualifier = rawToken.slice(0, lastDot);
                const prefix = endsWithDot ? '' : rawToken.slice(lastDot + 1);
                const prefixUpper = prefix.toUpperCase();
                const tableForCols = aliasMap[qualifier] || getLastPartTableName(qualifier);
                if (!tableForCols) { closeAutocomplete(); return; }

                _acReplaceStart = cursorPos - prefix.length;
                _acReplaceEnd = cursorPos;

                const cols = _acColumns[tableForCols] || [];
                const renderCols = function(colList) {
                    if (reqId !== _acReqSeq) return;
                    const list = (colList || []).filter(function(c) {
                        const u = (c || '').toUpperCase();
                        return prefixUpper ? u.startsWith(prefixUpper) : true;
                    });
                    list.slice(0, 20).forEach(function(c) { items.push(__makeItem(c, 'col', c)); });
                    if (!items.length) { closeAutocomplete(); return; }
                    _acItems = __dedupeItems(items);
                    _acSelected = 0;
                    renderAutocomplete();
                };

                if (!cols.length) {
                    loadTableColumns(tableForCols).then(function(loaded) { renderCols(loaded); });
                    return;
                }
                renderCols(cols);
                return;
            }

            if (wantsTables) {
                const prefixUpper = tokenUpper;
                _acTableNames
                    .filter(function(t) {
                        const u = (t || '').toUpperCase();
                        return prefixUpper ? u.startsWith(prefixUpper) : true;
                    })
                    .slice(0, 30)
                    .forEach(function(t) { items.push(__makeItem(t, 'tbl', t + ' ')); });
            }

            if (wantsColumns) {
                const refs = extractTableRefs(beforeNoToken);
                const preferredTbl = getPreferredTable(beforeNoToken);
                const prefixUpper = tokenUpper;
                const addColsFor = function(tableName, qualifierText, maxCount) {
                    const cols = _acColumns[tableName] || [];
                    const renderCols = function(colList) {
                        if (reqId !== _acReqSeq) return;
                        (colList || []).filter(function(c) {
                            const u = (c || '').toUpperCase();
                            return prefixUpper ? u.startsWith(prefixUpper) : true;
                        }).slice(0, maxCount).forEach(function(c) {
                            const label = qualifierText ? (qualifierText + '.' + c) : c;
                            const insertText = qualifierText ? (qualifierText + '.' + c) : c;
                            items.push(__makeItem(label, 'col', insertText));
                        });
                        _acItems = __dedupeItems(items).slice(0, 30);
                        if (!_acItems.length) { closeAutocomplete(); return; }
                        _acSelected = 0;
                        renderAutocomplete();
                    };
                    if (!cols.length) {
                        loadTableColumns(tableName).then(function(loaded) { renderCols(loaded); });
                        return false;
                    }
                    renderCols(cols);
                    return true;
                };

                if (!prefixUpper) items.push(__makeItem('*', 'col', '*'));

                if (preferredTbl) addColsFor(preferredTbl, '', 20);

                const extras = refs.filter(function(r) { return r && r.table && r.table !== preferredTbl; });
                extras.slice(0, 3).forEach(function(r) {
                    const q = r.alias || r.table;
                    if (q) items.push(__makeItem(q + '.', 'tbl', q + '.'));
                });

                extras.slice(0, 2).forEach(function(r) {
                    const q = r.alias || r.table;
                    if (r.table) addColsFor(r.table, q || '', 8);
                });
            }

            if (!wantsTables && !wantsColumns) {
                if (!rawToken || rawToken.length < 1) { closeAutocomplete(); return; }
            }

            SQL_KEYWORDS
                .filter(function(k) {
                    const u = (k || '').toUpperCase();
                    return tokenUpper ? (u.startsWith(tokenUpper) && u !== tokenUpper) : false;
                })
                .slice(0, 20)
                .forEach(function(k) { items.push(__makeItem(k, 'key', k + ' ')); });

            SQL_FUNCTIONS
                .filter(function(f) {
                    const u = (f || '').toUpperCase();
                    return tokenUpper ? (u.startsWith(tokenUpper) && u !== tokenUpper) : false;
                })
                .slice(0, 20)
                .forEach(function(f) { items.push(__makeItem(f, 'func', f)); });

            if (!wantsTables) {
                _acTableNames
                    .filter(function(t) {
                        const u = (t || '').toUpperCase();
                        return tokenUpper ? (u.startsWith(tokenUpper) && u !== tokenUpper) : false;
                    })
                    .slice(0, 20)
                    .forEach(function(t) { items.push(__makeItem(t, 'tbl', t)); });
            }

            if (!wantsColumns) {
                const preferredTbl = getPreferredTable(beforeNoToken);
                if (preferredTbl && rawToken) {
                    const cols2 = _acColumns[preferredTbl] || [];
                    const renderCols = function(colList) {
                        if (reqId !== _acReqSeq) return;
                        (colList || []).filter(function(c) { return c && c.toUpperCase().startsWith(tokenUpper); })
                            .slice(0, 12)
                            .forEach(function(c) { items.push(__makeItem(c, 'col', c)); });
                        _acItems = __dedupeItems(items).slice(0, 30);
                        if (!_acItems.length) { closeAutocomplete(); return; }
                        _acSelected = 0;
                        renderAutocomplete();
                    };
                    if (!cols2.length) {
                        loadTableColumns(preferredTbl).then(function(loaded) { renderCols(loaded); });
                        return;
                    }
                    renderCols(cols2);
                    return;
                }
            }

            if (rawToken) {
                _acHistory.filter(function(h) { return h && h.toUpperCase().startsWith(tokenUpper); }).slice(0, 5)
                    .forEach(function(h) { items.push(__makeItem(h, 'his', h)); });
            }

            const finalItems = __dedupeItems(items).slice(0, 30);
            if (!finalItems.length) { closeAutocomplete(); return; }
            _acItems = finalItems;
            _acSelected = 0;
            renderAutocomplete();
        }
        
        function renderAutocomplete() {
            const el = $$('sqlAutocomplete');
            if (!_acItems.length) { el.style.display = 'none'; return; }
            el.innerHTML = _acItems.map((item, i) => 
                `<div class="sql-autocomplete-item ${i === _acSelected ? 'selected' : ''}" data-index="${i}">
                    <span class="type-tag ${item.type}">${item.type.toUpperCase()}</span>
                    <span>${item.label}</span>
                </div>`
            ).join('');
            el.style.display = 'block';
            
            // 点击选择
            el.querySelectorAll('.sql-autocomplete-item').forEach((div, i) => {
                div.onclick = () => selectAutocomplete(i);
            });
        }
        
        function selectAutocomplete(index) {
            const item = _acItems[index];
            if (!item) return;
            const textarea = $$('sqlEditor');
            const text = textarea.value;
            const start = Math.max(0, Math.min(text.length, _acReplaceStart || 0));
            const end = Math.max(0, Math.min(text.length, _acReplaceEnd || start));
            const insert = String(item.insertText || item.label || '');
            textarea.value = text.substring(0, start) + insert + text.substring(end);
            const newPos = start + insert.length;
            textarea.selectionStart = textarea.selectionEnd = newPos;
            closeAutocomplete();
            textarea.focus();
        }
        
        function closeAutocomplete() {
            $$('sqlAutocomplete').style.display = 'none';
            _acItems = [];
            _acSelected = -1;
        }
        
        function initAutocomplete() {
            const textarea = $$('sqlEditor');
            textarea.addEventListener('input', e => {
                showAutocomplete(e.target.value, e.target.selectionStart);
            });
            textarea.addEventListener('keydown', e => {
                if (e.key === 'ArrowDown') {
                    e.preventDefault();
                    if (_acItems.length) {
                        _acSelected = (_acSelected + 1) % _acItems.length;
                        renderAutocomplete();
                    }
                } else if (e.key === 'ArrowUp') {
                    e.preventDefault();
                    if (_acItems.length) {
                        _acSelected = (_acSelected - 1 + _acItems.length) % _acItems.length;
                        renderAutocomplete();
                    }
                } else if (e.key === 'Enter' || e.key === 'Tab') {
                    if (_acItems.length && $$('sqlAutocomplete').style.display !== 'none') {
                        e.preventDefault();
                        selectAutocomplete(_acSelected);
                    }
                } else if (e.key === 'Escape') {
                    closeAutocomplete();
                }
            });
            textarea.addEventListener('blur', () => {
                setTimeout(closeAutocomplete, 200);
            });
        }
        
        async function executeQuery() {
            const sql = $$('sqlEditor').value.trim();
            if (!sql) return;
            $$('resultsContent').innerHTML = '<div class="loading"><div class="spinner"></div>' + __t('db_manager.executing', 'Running...') + '</div>';
            
            try {
                const r = await fetch('/api/db/execute', {
                    method: 'POST', headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ connection: currentConn.id, query: sql })
                });
                const data = await r.json();
                if (data.success) {
                    allResults = data.data?.rows || [];
                    currentPage = 1;
                    totalPages = Math.ceil(allResults.length / pageSize);
                    renderResults(data.data.headers || [], getPageData());
                    showStatus(__t('common.success', 'Success') + ' ' + allResults.length + ' ' + __t('db_manager.rows', 'rows'), false);
                    $$('resultsToolbar').style.display = allResults.length > pageSize ? 'flex' : 'none';
                    
                    // 保存到历史
                    if (sql.toLowerCase().startsWith('select') || sql.toLowerCase().startsWith('show') || sql.toLowerCase().startsWith('describe')) {
                        const history = JSON.parse(localStorage.getItem('db_sql_history') || '[]');
                        const filtered = history.filter(h => h.sql !== sql);
                        filtered.unshift({ sql: sql, time: Date.now() });
                        localStorage.setItem('db_sql_history', JSON.stringify(filtered.slice(0, 50)));
                    }
                } else {
                    showStatus(__t('db_manager.failed_prefix', 'Failed: ') + data.message, true);
                    $$('resultsContent').innerHTML = `<div class="results-empty" style="color:#ef4444;">${data.message}</div>`;
                    $$('resultsToolbar').style.display = 'none';
                }
            } catch (e) {
                showStatus(__t('db_manager.failed_prefix', 'Failed: ') + e.message, true);
                $$('resultsContent').innerHTML = `<div class="results-empty" style="color:#ef4444;">${e.message}</div>`;
            }
        }
        
        // AI SQL 生成
        let aiHistoryCache = null;

async function loadAiHistory() {
    if (aiHistoryCache) return aiHistoryCache;
    try {
        const res = await fetch('/db/ai-history');
        const data = await res.json();
        aiHistoryCache = (data.data && data.data.history) || [];
        return aiHistoryCache;
    } catch(e) { return []; }
}

async function toggleAiHistory() {
    const dropdown = document.getElementById('aiHistoryDropdown');
    if (dropdown.classList.contains('show')) {
        dropdown.classList.remove('show');
        return;
    }
    const history = await loadAiHistory();
    if (history.length === 0) {
        dropdown.innerHTML = '<div style="padding:20px;text-align:center;color:#666;font-size:12px;">' + __t('db_manager.no_history', 'No history') + '</div>';
    } else {
        dropdown.innerHTML = history.slice(0, 10).map(item => `
            <div class="ai-history-item" onclick="selectAiHistory('${item.prompt.replace(/'/g, "\'")}', '${item.sql.replace(/'/g, "\'").replace(/\n/g, " ")}')">
                <div class="prompt">${item.prompt}</div>
                <div class="sql">${item.sql}</div>
                <div class="time">${item.time || ''}</div>
            </div>
        `).join('');
    }
    dropdown.classList.add('show');
}

function selectAiHistory(prompt, sql) {
    document.getElementById('aiPromptInput').value = prompt;
    document.getElementById('aiHistoryDropdown').classList.remove('show');
    // 如果需要同时设置SQL
    if (sql) {
        const sqlInput = document.getElementById('sqlInput');
        if (sqlInput) sqlInput.value = sql;
    }
}

// 点击其他地方关闭下拉
document.addEventListener('click', function(e) {
    const dropdown = document.getElementById('aiHistoryDropdown');
    const combo = document.getElementById('aiPromptCombo');
    if (dropdown && combo && !combo.contains(e.target)) {
        dropdown.classList.remove('show');
    }
});

async function generateSqlFromPrompt() {
            const prompt = $$('aiPromptInput').value.trim();
            const currentSql = $$('sqlEditor').value.trim();
            
            if (!prompt) {
                showStatus(__t('db_manager.ai_prompt_required', 'Please enter AI prompt'), true);
                return;
            }
            
            const btn = $$('btnAiGenerate');
            const originalText = btn.innerHTML;
            btn.disabled = true;
            btn.innerHTML = '<span class="ai-loading"></span>';
            
            try {
                // 获取当前连接的数据库信息
                const connId = currentConn?.id;
                if (!connId) {
                    showStatus(__t('db_manager.select_connection_first', 'Please select a database connection first'), true);
                    return;
                }
                
                // 构建请求数据
                const requestData = {
                    prompt: prompt,
                    current_sql: currentSql,
                    connection: {
                        id: connId,
                        engine: currentConn.engine || 'mysql',
                        database: currentConn.database || ''
                    },
                    table: currentTable || '',
                    schema: []
                };
                
                // 如果有选中的表，获取表结构
                if (currentTable) {
                    try {
                        const schemaResp = await fetch('/api/db/schema', {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json' },
                            body: JSON.stringify({ connection: connId, table: currentTable })
                        });
                        const schemaData = await schemaResp.json();
                        if (schemaData.success) {
                            requestData.schema = schemaData.data || [];
                        }
                    } catch (e) {
                        console.warn('获取表结构失败:', e);
                    }
                }
                
                const r = await fetch('/db/ai-generate-sql', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(requestData)
                });
                
                const data = await r.json();
                
                if (data.success && data.sql) {
                    $$('sqlEditor').value = data.sql;
                    $$('aiPromptInput').value = '';
                    showStatus(__t('db_manager.ai_generate_success', 'SQL generated ✓'), false);
                } else {
                    const errMsg = data.error?.message || data.message || data.error || __t('common.unknown', 'Unknown');
                    showStatus(__t('db_manager.ai_generate_failed_prefix', 'Generate failed: ') + errMsg, true);
                }
            } catch (e) {
                showStatus(__t('db_manager.ai_generate_failed_prefix', 'Generate failed: ') + e.message, true);
            } finally {
                btn.disabled = false;
                btn.innerHTML = originalText;
            }
        }
        
        // Ctrl+Enter 执行 SQL
        $$('sqlEditor').addEventListener('keydown', function(e) {
            if (e.ctrlKey && e.key === 'Enter') {
                e.preventDefault();
                executeQuery();
            }
            // Ctrl+Up/Down 历史导航
            if (e.ctrlKey && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
                e.preventDefault();
                if (e.key === 'ArrowUp') sqlHistoryPrev();
                else sqlHistoryNext();
            }
        });
        
        // Enter 生成 AI SQL
        $$('aiPromptInput').addEventListener('keydown', function(e) {
            if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                generateSqlFromPrompt();
            }
        });
        
        // AI Prompt 输入时保存
        $$('aiPromptInput').addEventListener('input', function(e) {
            saveDbUiState({ ai_prompt: e.target.value || '' }, false);
        });
        
        function getPageData() {
            const start = (currentPage - 1) * pageSize;
            return allResults.slice(start, start + pageSize);
        }
        
        function goToPage(page) {
            if (page < 1 || page > totalPages) return;
            currentPage = page;
            const headers = $$('resultsContent').querySelectorAll('thead th');
            const headerArr = Array.from(headers).map(th => th.textContent);
            renderResults(headerArr, getPageData());
        }
        
        function renderResults(headers, rows) {
            if (rows.length === 0) {
                $$('resultsContent').innerHTML = '<div class="results-empty">' + __t('db_manager.no_data', 'No data') + '</div>';
                $$('pageInfo').textContent = '0 / 0';
                return;
            }
            
            // 保存原始数据用于编辑
            window._tableHeaders = headers;
            window._tableRows = rows;
            
            // 检查 Tabulator 是否加载
            if (typeof Tabulator === 'undefined') {
                // 回退到原生表格
                renderResultsNative(headers, rows);
                return;
            }
            
            // 销毁旧的 Tabulator 实例
            if (window._resultsTable) {
                window._resultsTable.destroy();
            }
            
            // 准备 Tabulator 数据（将数组转换为对象数组）
            const tableData = rows.map((row, ri) => {
                const obj = { _rowIndex: ri };
                headers.forEach((h, ci) => {
                    obj[h] = row[ci];
                });
                return obj;
            });
            
            // 构建列定义
            const columns = headers.map(h => ({
                title: h,
                field: h,
                editor: "input",
                editable: true,
                headerFilter: "input",
                headerFilterPlaceholder: "筛选...",
                minWidth: 100,
                cellDblClick: function(e, cell) {
                    cell.edit();
                }
            }));
            
            $$('resultsContent').innerHTML = '<div id="resultsTable" style="width:100%;height:100%;"></div>';
            
            console.log('Initializing Tabulator with', tableData.length, 'rows');
            
            // 初始化 Tabulator
            window._resultsTable = new Tabulator("#resultsTable", {
                data: tableData,
                columns: columns,
                height: "100%",
                layout: "fitColumns",
                virtualDom: true,
                pagination: "local",
                paginationSize: 50,
                movableColumns: false,
                langs: {
                    "zh-cn": {
                        "pagination": {
                            "first": "首页",
                            "prev": "上一页",
                            "next": "下一页",
                            "last": "末页",
                            "page_size": "每页条数"
                        },
                        "headerfilters": {
                            "default": __t('db_manager.filter_placeholder', 'Filter...')
                        }
                    }
                },
                locale: "zh-cn",
                cellEditStarted: function(cell) {
                    // 编辑时高亮边框
                    cell.getElement().style.background = "#1a1a2e";
                    cell.getElement().style.boxShadow = "inset 0 0 0 2px #e94560";
                },
                cellEditCancelled: function(cell) {
                    cell.getElement().style.background = "";
                    cell.getElement().style.boxShadow = "";
                },
                cellEdited: function(cell) {
                    console.log('Tabulator cellEdited triggered!');
                    
                    cell.getElement().style.background = "";
                    cell.getElement().style.boxShadow = "";
                    
                    // 获取行索引和列名
                    var rowData = cell.getData();
                    var rowIndex = rowData._rowIndex;
                    var colName = cell.getColumn().getField();
                    var newValue = cell.getValue();
                    
                    // 获取列索引
                    var headerList = window._tableHeaders || [];
                    var colIndex = headerList.indexOf(colName);
                    
                    console.log('Cell edited:', rowIndex, colName, newValue, colIndex);
                    
                    if (colIndex >= 0 && rowIndex !== undefined) {
                        // 更新本地数据
                        if (window._tableRows && window._tableRows[rowIndex]) {
                            window._tableRows[rowIndex][colIndex] = newValue;
                        }
                        // 提交更新 - 使用 Tabulator 的行数据
                        console.log('Submitting edit:', rowIndex, colIndex, colName, newValue);
                        // 直接构建更新 SQL
                        var sql = "UPDATE `" + currentTable + "` SET `" + colName + "` = " + (newValue === '' ? 'NULL' : typeof newValue === 'number' ? newValue : "'" + newValue.replace(/'/g, "''") + "'") + " WHERE ";
                        
                        // 获取原始行数据用于 WHERE 条件
                        var originalRow = window._tableRows && window._tableRows[rowIndex];
                        if (!originalRow) {
                            console.error('Original row not found:', rowIndex);
                            return;
                        }
                        
                        var pkParts = [];
                        var headerList = window._tableHeaders || [];
                        headerList.forEach(function(h, i) {
                            var val = originalRow[i];
                            if (typeof val === 'number') {
                                pkParts.push("`" + h + "` = " + val);
                            } else {
                                pkParts.push("`" + h + "` = '" + String(val || '').replace(/'/g, "''") + "'");
                            }
                        });
                        sql += pkParts.join(' AND ');
                        
                        console.log('SQL:', sql);
                        
                        // 发送更新请求
                        fetch('/api/db/execute', {
                            method: 'POST',
                            headers: {'Content-Type': 'application/json'},
                            body: JSON.stringify({ connection: currentConn.id, query: sql })
                        }).then(function(r) { return r.json(); }).then(function(data) {
                            console.log('Update result:', data);
                            if (data.success) {
                                showStatus(__t('db_manager.update_success', '✅ Update successful'), false);
                            } else {
                                showStatus(__t('db_manager.update_failed_prefix', '❌ Update failed: ') + (data.message || data.error), true);
                            }
                        }).catch(function(e) { 
                            console.error('Update error:', e);
                            showStatus(__t('db_manager.update_failed_prefix', '❌ Update failed: ') + e.message, true); 
                        });
                    }
                }
            });
            
            $$('pageInfo').textContent = __t('db_manager.total', 'Total') + ' ' + rows.length + ' ' + __t('db_manager.entries', 'items');
        }
        
        // 回退到原生表格实现
        function renderResultsNative(headers, rows) {
            $$('resultsContent').innerHTML = `<table class="data-table" style="width:100%"><thead><tr>${headers.map(h => `<th>${h}</th>`).join('')}</tr></thead><tbody>${rows.map((row, ri) => `<tr>${row.map((cell, ci) => `<td onclick="editCell(this, ${ri}, ${ci})">${cell||''}</td>`).join('')}</tr>`).join('')}</tbody></table>`;
            $$('pageInfo').textContent = __t('db_manager.total', 'Total') + ' ' + rows.length + ' ' + __t('db_manager.entries', 'items') + ' (' + __t('db_manager.tabulator_not_loaded', 'Tabulator not loaded') + ')';
        }
        
        // 单元格编辑
        function editCell(td, rowIdx, colIdx) {
            if (td.querySelector('input')) return; // 已在编辑
            const value = td.textContent;
            const input = document.createElement('input');
            input.type = 'text';
            input.value = value;
            input.style.cssText = 'width:100%;border:none;background:#fff;color:#333;padding:2px 4px;border-radius:3px;';
            input.onblur = () => submitCellEdit(td, rowIdx, colIdx, input.value);
            input.onkeydown = e => { if (e.key === 'Enter') input.blur(); if (e.key === 'Escape') { td.textContent = value; } };
            td.textContent = '';
            td.appendChild(input);
            input.focus();
            input.select();
        }
        
        function submitCellEdit(td, rowIdx, colIdx, newValue) {
            const oldValue = allResults[rowIdx][colIdx];
            if (oldValue === newValue) { 
                if (td) td.textContent = oldValue; 
                return; 
            }
            
            if (!currentTable) { showStatus(__t('db_manager.update_no_table', 'Cannot update: no table selected'), true); return; }
            
            // 使用保存的表头
            const headerList = window._tableHeaders || [];
            const colName = headerList[colIdx];
            if (!colName) { showStatus(__t('db_manager.update_unknown_column', 'Cannot update: unknown column'), true); return; }
            
            const sql = "UPDATE `" + currentTable + "` SET `" + colName + "` = " + (newValue === '' ? 'NULL' : typeof newValue === 'number' ? newValue : "'" + newValue.replace(/'/g, "''") + "'") + " WHERE ";
            
            // 通过主键定位
            const pkParts = [];
            headerList.forEach(function(h, i) {
                var val = allResults[rowIdx][i];
                if (typeof val === 'number') {
                    pkParts.push("`" + h + "` = " + val);
                } else {
                    pkParts.push("`" + h + "` = '" + String(val || '').replace(/'/g, "''") + "'");
                }
            });
            
            fetch('/api/db/execute', {
                method: 'POST', headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({ connection: currentConn.id, query: sql + pkParts.join(' AND ') })
            }).then(r => r.json()).then(data => {
                if (data.success) {
                    allResults[rowIdx][colIdx] = newValue;
                    if (td) td.textContent = newValue;
                    showStatus(__t('db_manager.update_success_plain', 'Update successful'), false);
                } else {
                    // 回滚数据
                    if (window._resultsTable) {
                        const rowData = window._resultsTable.getData();
                        rowData[rowIdx][colName] = oldValue;
                        window._resultsTable.setData(rowData);
                    }
                    showStatus(__t('db_manager.update_failed_prefix_plain', 'Update failed: ') + (data.message || data.error), true);
                }
            }).catch(e => { 
                showStatus(__t('db_manager.update_failed_prefix_plain', 'Update failed: ') + e.message, true); 
            });
        }
        
        
        function quickQuery(sql) {
            if (!currentConn) { showStatus(__t('db_manager.select_table_first', 'Please select a table first'), true); return; }
            $$('sqlEditor').value = sql;
            $$('sqlEditorPanel').classList.add('open');
            $$('sqlToggle').classList.add('open');
            saveDbUiState({ sql_open: true, sql: String(sql || '') });
            executeQuery();
        }
        
        function exportData(format) {
            if (allResults.length === 0) { showStatus(__t('db_manager.no_export_data', 'No data to export'), true); return; }
            const headers = $$('resultsContent').querySelectorAll('thead th');
            const headerArr = Array.from(headers).map(th => th.textContent);
            let content = '', filename = `export_${Date.now()}`;
            
            if (format === 'csv') {
                content = headerArr.map(h => `"${h}"`).join(',') + '\n';
                allResults.forEach(row => content += row.map(cell => `"${(cell||'').replace(/"/g, '""')}"`).join(',') + '\n');
                filename += '.csv';
            } else if (format === 'xls') {
                content = '<html><meta charset="utf-8"><table border="1"><tr>' + headerArr.map(h => `<th style="background:#eee;padding:4px;">${h}</th>`).join('') + '</tr>' + allResults.map(row => '<tr>' + row.map(cell => `<td style="padding:4px;">${cell||''}</td>`).join('') + '</tr>').join('') + '</table></html>';
                filename += '.xls';
            } else if (format === 'sql') {
                const table = currentTable || 'table_name';
                content = `-- ${table}\n`;
                allResults.forEach(row => {
                    const vals = row.map(cell => cell === null ? 'NULL' : typeof cell === 'number' ? cell : `'${String(cell).replace(/'/g, "''")}'`).join(', ');
                    content += `INSERT INTO \`${table}\` VALUES (${vals});\n`;
                });
                filename += '.sql';
            }
            const blob = new Blob([content], {type: format === 'csv' ? 'text/csv' : 'application/vnd.ms-excel'});
            const a = document.createElement('a');
            a.href = URL.createObjectURL(blob);
            a.download = filename;
            a.click();
            URL.revokeObjectURL(blob);
            showStatus(__t('db_manager.exported_prefix', 'Exported ') + allResults.length + ' ' + __t('db_manager.rows', 'rows'), false);
        }
        
        function switchResultsTab(tab) {
            document.querySelectorAll('.results-tab').forEach(t => t.classList.remove('active'));
            $$('tab' + tab.charAt(0).toUpperCase() + tab.slice(1)).classList.add('active');
            $$('resultsContent').style.display = tab === 'results' ? 'block' : 'none';
            $$('schemaPanel').style.display = tab === 'schema' ? 'block' : 'none';
            if (tab === 'schema' && currentTable) loadTableSchema();
            currentResultsTab = tab;
            saveDbUiState({ tab: tab });
        }
        
        async function loadTableSchema() {
            if (!currentConn || !currentTable) return;
            $$('schemaPanel').innerHTML = '<div class="loading"><div class="spinner"></div>' + __t('common.loading', 'Loading...') + '</div>';
            try {
                const r = await fetch(`/api/db/${currentConn.id}/table/${currentTable}/schema`);
                const data = await r.json();
                if (data.success) {
                    const cols = data.data?.columns || [];
                    $$('schemaPanel').innerHTML = cols.length ? `<table class="data-table"><thead><tr><th>${__t('db_manager.schema_column', 'Column')}</th><th>${__t('db_manager.schema_type', 'Type')}</th><th>${__t('db_manager.schema_nullable', 'Nullable')}</th><th>${__t('db_manager.schema_key', 'Key')}</th></tr></thead><tbody>` + 
                        cols.map(c => `<tr><td>${c.field}</td><td>${c.type}</td><td>${c.null === 'YES' ? '✓' : '✗'}</td><td>${c.key || ''}</td></tr>`).join('') + '</tbody></table>' : '<div class="results-empty">' + __t('db_manager.no_schema', 'No schema') + '</div>';
                }
            } catch (e) { $$('schemaPanel').innerHTML = '<div class="results-empty">' + __t('db_manager.load_failed', 'Load failed') + '</div>'; }
        }
        
        function showStatus(msg, error) { 
            $$('statusBar').innerHTML = `<span class="${error ? 'error' : 'success'}">${msg}</span>`;
            // 同时显示顶部toast
            const toast = $$('toast');
            toast.textContent = msg;
            toast.className = 'show ' + (error ? 'error' : 'success');
            setTimeout(() => { toast.className = ''; }, 2500);
        }
        
        function showConfirm(title, msg, onConfirm, danger) {
            $$('confirmTitle').textContent = title;
            $$('confirmMessage').textContent = msg;
            __confirmCallback = onConfirm;
            $$('confirmBtn').className = danger ? 'confirm' : '';
            $$('confirmBtn').textContent = danger ? __t('common.delete', 'Delete') : __t('common.confirm', 'Confirm');
            $$('confirmBackdrop').style.display = 'flex';
        }
        function performConfirm() { if (__confirmCallback) __confirmCallback(); closeConfirmModal(); }
        function closeConfirmModal() { $$('confirmBackdrop').style.display = 'none'; }
        
        function copyToClipboard(text) {
            if (typeof clipboard !== 'undefined' && clipboard.copy) clipboard.copy(text).then(() => showStatus(__t('common.copied', 'Copied'), false)).catch(() => fallbackCopy(text));
            else fallbackCopy(text);
        }
        function fallbackCopy(text) {
            const ta = document.createElement('textarea');
            ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0';
            document.body.appendChild(ta);
            ta.select();
            try { document.execCommand('copy'); showStatus(__t('common.copied', 'Copied'), false); } catch (e) { showStatus(__t('common.copy_failed_detail', 'Failed to copy to clipboard'), true); }
            document.body.removeChild(ta);
        }
        
        // 添加连接
        function openAddModal() {
            $$('dbEngine').value = 'mysql'; $$('dbName').value = ''; $$('dbHost').value = ''; $$('dbPort').value = '3306';
            $$('dbUser').value = ''; $$('dbPass').value = ''; $$('dbDatabase').value = ''; $$('dbSqlitePath').value = '';
            $$('dbUrl').value = '';
            updateEngineFields();
            $$('addModal').classList.add('show');
        }
        function closeAddModal() { $$('addModal').classList.remove('show'); }
        function updateEngineFields() {
            const eng = $$('dbEngine').value;
            document.querySelectorAll('.host-field').forEach(el => el.style.display = eng === 'sqlite' ? 'none' : 'block');
            document.querySelectorAll('.sqlite-field').forEach(el => el.style.display = eng === 'sqlite' ? 'block' : 'none');
        }

        function normalizeEngineFromScheme(scheme) {
            var s = String(scheme || '').toLowerCase();
            if (!s) return '';
            var base = s.split('+')[0];
            if (base === 'postgres') base = 'postgresql';
            if (base === 'sqlserver') base = 'mssql';
            if (base === 'mssql') base = 'mssql';
            if (base === 'mysql') base = 'mysql';
            if (base === 'mariadb') base = 'mariadb';
            if (base === 'sqlite') base = 'sqlite';
            if (base === 'oracle') base = 'oracle';
            return base;
        }

        function parseDbUrl(raw) {
            var input = String(raw || '').trim();
            if (!input) return null;
            if (input.toLowerCase().startsWith('sqlite:')) {
                var m4 = input.match(/^sqlite:(\/\/\/\/)(.+)$/i);
                var m3 = input.match(/^sqlite:(\/\/\/)(.+)$/i);
                var m2 = input.match(/^sqlite:(\/\/)(.+)$/i);
                var rest = m4 ? ('/' + m4[2]) : (m3 ? m3[2] : (m2 ? m2[2] : input.replace(/^sqlite:/i, '')));
                var path = '';
                try { path = decodeURIComponent(rest); } catch (e) { path = rest; }
                return { engine: 'sqlite', sqlitePath: path };
            }

            var u = null;
            try { u = new URL(input); } catch (e) { return null; }
            var engine = normalizeEngineFromScheme((u && u.protocol ? u.protocol.replace(':', '') : ''));
            if (!engine) return null;
            var database = '';
            try { database = decodeURIComponent(String(u.pathname || '').replace(/^\/+/, '')); } catch (e) { database = String(u.pathname || '').replace(/^\/+/, ''); }
            var port = u.port ? String(u.port) : '';
            return {
                engine: engine,
                host: u.hostname || '',
                port: port,
                user: u.username || '',
                password: u.password || '',
                database: database
            };
        }

        function applyDbUrl(showErr) {
            var url = $$('dbUrl') ? $$('dbUrl').value : '';
            var parsed = parseDbUrl(url);
            if (!parsed) {
                if (showErr) showStatus(__t('db_manager.url_parse_failed', 'Failed to parse URL'), true);
                return;
            }

            var eng = parsed.engine || '';
            if (eng && $$('dbEngine')) $$('dbEngine').value = eng;
            updateEngineFields();

            if (eng === 'sqlite') {
                if ($$('dbSqlitePath')) $$('dbSqlitePath').value = parsed.sqlitePath || '';
                if ($$('dbPort')) $$('dbPort').value = '0';
                return;
            }

            if ($$('dbHost')) $$('dbHost').value = parsed.host || $$('dbHost').value;
            if ($$('dbUser')) $$('dbUser').value = parsed.user || $$('dbUser').value;
            if ($$('dbPass')) $$('dbPass').value = parsed.password || $$('dbPass').value;
            if ($$('dbDatabase')) $$('dbDatabase').value = parsed.database || $$('dbDatabase').value;
            if ($$('dbPort')) $$('dbPort').value = parsed.port || String(ENGINE_CONFIG[eng]?.port || $$('dbPort').value || '');
        }
        
        async function testConnection() {
            const eng = $$('dbEngine').value;
            const data = {name:$$('dbName').value || 'test', engine:eng, host:eng==='sqlite'?'':$$('dbHost').value, port:String($$('dbPort').value||ENGINE_CONFIG[eng]?.port||3306), 
                user:eng==='sqlite'?'':$$('dbUser').value, password:eng==='sqlite'?'':$$('dbPass').value, database:eng==='sqlite'?$$('dbSqlitePath').value:$$('dbDatabase').value};
            if (eng !== 'sqlite' && (!data.host || !data.user)) { showStatus(__t('db_manager.required_fields', 'Please fill required fields'), true); return; }
            showStatus(__t('db_manager.testing_connection', 'Testing connection...'), false);
            try {
                
                const r = await fetch('/api/db/test', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(data)});
                
                const result = await r.json();
                if (!r.ok || !result.success) { 
                    const errMsg = result.error?.message || result.error || __t('db_manager.connect_failed', 'Connection failed');
                    showStatus(errMsg.substring(0, 100), true); return; 
                }
                showStatus(__t('db_manager.connect_success', 'Connected!'), false);
            } catch (e) { showStatus(__t('db_manager.connect_failed_prefix', 'Connection failed: ') + e.message, true); }
        }
        async function testAndSave() {
            const eng = $$('dbEngine').value;
            const data = {name:$$('dbName').value, engine:eng, host:eng==='sqlite'?'':$$('dbHost').value, port:String($$('dbPort').value||ENGINE_CONFIG[eng]?.port||3306), 
                user:eng==='sqlite'?'':$$('dbUser').value, password:eng==='sqlite'?'':$$('dbPass').value, database:eng==='sqlite'?$$('dbSqlitePath').value:$$('dbDatabase').value};
            if (!data.name) { showStatus(__t('db_manager.name_required', 'Please enter a name'), true); return; }
            if (eng !== 'sqlite' && (!data.host || !data.user)) { showStatus(__t('db_manager.required_fields', 'Please fill required fields'), true); return; }
            try {
                const r = await fetch('/api/db/connect', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(data)});
                const result = await r.json();
                if (!r.ok || !result.success) { showStatus(result.error || __t('db_manager.save_failed', 'Save failed'), true); return; }
                closeAddModal(); loadTree(); loadConnections(); showStatus(__t('db_manager.saved', 'Saved'), false);
            } catch (e) { showStatus(__t('db_manager.save_failed_prefix', 'Save failed: ') + e.message, true); }
        }
        
        // 编辑连接
        async function openEditModal(id) {
            const conn = connections.find(c => c.id === id);
            if (!conn) return;
            $$('editConnId').value = id; $$('editDbName').value = conn.name || '';
            $$('editDbHost').value = conn.host || ''; $$('editDbPort').value = conn.port || '';
            $$('editDbUser').value = conn.user || ''; $$('editDbPass').value = '';
            $$('editDbDatabase').value = conn.database || ''; $$('editDbSqlitePath').value = conn.engine === 'sqlite' ? conn.database : '';
            const url = `${conn.engine || 'mysql'}://${conn.user||''}:***@${conn.host||''}:${conn.port||3306}/${conn.database||''}`;
            $$('editDbUrl').value = url;
            $$('editModal').classList.add('show');
        }
        function closeEditModal() { $$('editModal').classList.remove('show'); }
        
        async function saveEditConnection() {
            const id = $$('editConnId').value;
            const eng = connections.find(c => c.id === id)?.engine || 'mysql';
            const data = {name:$$('editDbName').value, engine:eng, host:$$('editDbHost').value, port:String($$('editDbPort').value), user:$$('editDbUser').value, password:$$('editDbPass').value, database:$$('editDbDatabase').value};
            try {
                const r = await fetch(`/api/db/connection/${id}`, {method:'PUT', headers:{'Content-Type':'application/json'}, body:JSON.stringify(data)});
                const result = await r.json();
                if (!r.ok || !result.success) { showStatus(result.error || __t('db_manager.save_failed', 'Save failed'), true); return; }
                closeEditModal(); loadTree(); loadConnections(); showStatus(__t('db_manager.updated', 'Updated'), false);
            } catch (e) { showStatus(__t('db_manager.save_failed', 'Save failed'), true); }
        }
        
        async function testEditConnection() {
            const id = $$('editConnId').value;
            const conn = connections.find(c => c.id === id);
            const eng = conn?.engine || 'mysql';
            const isSqlite = eng === 'sqlite';
            // Use input password, or fallback to saved password
            const inputPwd = $$('editDbPass').value;
            const savedPwd = conn?.password || '';
            const password = inputPwd || savedPwd;
            const data = {
                conn_id: id,
                name:$$('editDbName').value || 'test', 
                engine:eng, 
                host:isSqlite?'':$$('editDbHost').value, 
                port:String($$('editDbPort').value||3306),
                user:isSqlite?'':$$('editDbUser').value, 
                password:password, 
                database:isSqlite?$$('editDbSqlitePath').value:$$('editDbDatabase').value
            };
            showStatus(__t('db_manager.testing_connection', 'Testing connection...'), false);
            try {
                
                const r = await fetch('/api/db/test', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(data)});
                
                const result = await r.json();
                if (!r.ok || !result.success) { 
                    const errMsg = result.error?.message || result.error || __t('db_manager.connect_failed', 'Connection failed');
                    showStatus(errMsg.substring(0, 100), true); return; 
                }
                showStatus(__t('db_manager.connect_success', 'Connected!'), false);
            } catch (e) { showStatus(__t('db_manager.connect_failed_prefix', 'Connection failed: ') + e.message, true); }
        }
        
        function deleteConnection(id) {
            showConfirm(__t('db_manager.delete_connection_title', 'Delete Connection'), __t('db_manager.delete_connection_confirm', 'Are you sure you want to delete this connection?'), async () => {
                try {
                    await fetch(`/api/db/connections/${id}`, {method:'DELETE'});
                    currentConn = null; currentDb = null; currentTable = null;
                    loadTree(); loadConnections(); updateBreadcrumb(); showStatus(__t('db_manager.deleted', 'Deleted'), false);
                } catch (e) { showStatus(__t('db_manager.delete_failed', 'Delete failed'), true); }
            }, true);
        }
        
        async function loadConnections() {
            const r = await fetch('/api/db/connections');
            const data = await r.json();
            if (data.success) connections = data.data?.connections || [];
        }

        function __downloadJson(filename, obj) {
            const blob = new Blob([JSON.stringify(obj, null, 2)], { type: 'application/json;charset=utf-8' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }

        async function exportDbConnections() {
            showStatus(__t('db_manager.exporting', 'Exporting...'), false);
            try {
                const r = await fetch('/api/db/connections/export');
                const data = await r.json();
                if (!r.ok || !data.success) {
                    const errMsg = data.error?.message || data.error || __t('db_manager.export_failed', 'Export failed');
                    showStatus(errMsg, true);
                    return;
                }
                __downloadJson('db_connections.json', data.data || {});
                showStatus(__t('db_manager.exported', 'Exported'), false);
            } catch (e) {
                showStatus(__t('db_manager.export_failed_prefix', 'Export failed: ') + (e && e.message ? e.message : 'Error'), true);
            }
        }

        function triggerImportDbConnections() {
            const input = $$('dbConnImportFile');
            if (!input) return;
            input.value = '';
            input.click();
        }

        async function onImportDbConnectionsFileChange(ev) {
            const file = ev && ev.target && ev.target.files ? ev.target.files[0] : null;
            if (!file) return;
            try {
                const text = await file.text();
                const json = JSON.parse(text);
                showStatus(__t('db_manager.importing', 'Importing...'), false);
                const r = await fetch('/api/db/connections/import', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(json)
                });
                const data = await r.json();
                if (!r.ok || !data.success) {
                    const errMsg = data.error?.message || data.error || __t('db_manager.import_failed', 'Import failed');
                    showStatus(errMsg, true);
                    return;
                }
                await loadConnections();
                await loadTree();
                showStatus(__t('db_manager.imported_prefix', 'Imported ') + (data.data?.imported || 0) + ' ' + __t('db_manager.entries', 'items'), false);
            } catch (e) {
                showStatus(__t('db_manager.import_failed_prefix', 'Import failed: ') + (e && e.message ? e.message : 'Error'), true);
            }
        }

        if ($$('dbEngine')) $$('dbEngine').addEventListener('change', function() { updateEngineFields(); });
        if ($$('dbUrl')) {
            $$('dbUrl').addEventListener('change', function() { applyDbUrl(false); });
            $$('dbUrl').addEventListener('keydown', function(e) { if (e && e.key === 'Enter') { e.preventDefault(); applyDbUrl(false); } });
            $$('dbUrl').addEventListener('paste', function() { setTimeout(function() { applyDbUrl(false); }, 0); });
        }
        
        document.addEventListener('keydown', e => { if ((e.ctrlKey||e.metaKey) && e.key==='Enter') executeQuery(); });
        if ($$('sqlEditor')) {
            $$('sqlEditor').addEventListener('input', function() {
                saveDbUiState({ sql: String($$('sqlEditor').value || '') }, false);
            });
            $$('sqlEditor').addEventListener('blur', function() {
                saveDbUiState({ sql: String($$('sqlEditor').value || '') }, true);
            });
        }
        Promise.all([loadDbUiState(), loadConnections()])
            .then(loadTree)
            .then(() => { updateAutocompleteData(); initAutocomplete(); })
            .then(() => { applyDbUiState(); });
        
        // 更新i18n翻译
        if (typeof I18n !== 'undefined') {
            I18n.updatePage();
        }
    </script>
</body>
</html>
