chore(jcef): 更新缓存数据库日志文件

- 更新 shared_proto_db/metadata/000003.log 文件内容
- 更新 Site Characteristics Database/00003.log 文件内容
- 添加新的数据库条目和元数据记录
- 保持数据库文件格式的一致性
- 删除Vivid2D的内容
- 重写启动加载界面
This commit is contained in:
2026-01-02 17:12:54 +08:00
parent 75bdca05f2
commit 8de2b0f2fe
261 changed files with 4375 additions and 59810 deletions

481
javascript/SQLTerminal.html Normal file
View File

@@ -0,0 +1,481 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>SQL Command Line</title>
<style>
:root {
--bg-color: #0c0c0c;
--text-color: #cccccc;
--caret-color: #ffffff;
--selection-bg: #264f78;
--menu-bg: #2b2d30;
--highlight-active: #0d293e;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: 'Consolas', 'Courier New', monospace;
font-size: 16px;
margin: 0;
padding: 10px;
height: 100vh;
box-sizing: border-box;
overflow-y: auto;
word-break: break-all;
}
::selection { background: var(--selection-bg); color: white; }
#terminal { width: 100%; min-height: 100%; display: flex; flex-direction: column; padding-bottom: 50px; }
#output-area { white-space: pre-wrap; cursor: text; }
.input-line { display: flex; margin-top: 5px; position: relative; }
.prompt { color: #cccccc; margin-right: 8px; font-weight: bold; user-select: none; white-space: pre; }
.continuation-prompt { color: #cccccc; margin-right: 8px; user-select: none; white-space: pre; }
input[type="text"] {
background: transparent; border: none; color: #ffffff;
font-family: inherit; font-size: inherit; flex: 1;
outline: none; padding: 0; margin: 0; caret-color: var(--caret-color);
}
.welcome-msg { margin-bottom: 20px; color: #888; }
.error-text { color: #ff5555; }
/* --- 自动补全样式 --- */
#suggestion-box {
position: absolute; display: none; background-color: #2b2d30;
border: 1px solid #5f6165; box-shadow: 0 5px 15px rgba(0,0,0,0.6);
z-index: 999; max-height: 200px; overflow-y: auto; min-width: 250px;
border-radius: 3px;
}
.suggestion-item {
padding: 5px 10px; cursor: pointer; font-size: 14px;
color: #a9b7c6; display: flex; align-items: center;
}
.suggestion-item.active { background-color: var(--highlight-active); color: #ffffff; }
.icon {
display: inline-block; width: 16px; height: 16px; margin-right: 8px;
font-size: 10px; text-align: center; line-height: 16px; border-radius: 2px;
font-weight: bold; color: #000;
}
.icon-k { background-color: #ffc66d; } /* Keyword */
.icon-t { background-color: #4a88c7; color: #fff; } /* Table */
.icon-c { background-color: #9876aa; color: #fff; } /* Column */
.detail-text { margin-left: auto; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div id="terminal">
<div class="welcome-msg">
Axis SQL Client [Version 2.0.0]<br>
(c) 2024 Axis Innovators.<br>
Loaded local database mode.<br>
<br>
Commands end with ; <br>
Type 'help' for help. 'cls' to clear.<br>
</div>
<div id="output-area"></div>
<div class="input-line">
<span id="prompt-span" class="prompt">SQL&gt;</span>
<input type="text" id="cmd-input" autocomplete="off" spellcheck="false" autofocus>
<div id="suggestion-box"></div>
</div>
</div>
<script>
// --- 核心状态 ---
const outputArea = document.getElementById('output-area');
const input = document.getElementById('cmd-input');
const promptSpan = document.getElementById('prompt-span');
const suggestionBox = document.getElementById('suggestion-box');
let history = [];
let historyIndex = -1;
// 多行命令缓冲区
let commandBuffer = "";
let isMultiLineMode = false;
// 数据库元数据 (由 Java 填充)
let dbSchema = {
tables: [], // ["student", "logs"]
columns: {}, // {"student": ["id", "name"], "logs": ["id", "msg"]}
allColumns: [] // flat list of all columns for quick search
};
// 静态关键字库
const keywords = [
"SELECT", "FROM", "WHERE", "INSERT", "INTO", "VALUES", "UPDATE", "SET",
"DELETE", "CREATE", "TABLE", "DROP", "ALTER", "ORDER", "BY", "GROUP",
"LIMIT", "JOIN", "LEFT", "RIGHT", "INNER", "OUTER", "ON", "AND", "OR",
"NOT", "NULL", "AS", "DISTINCT", "COUNT", "MAX", "MIN", "AVG", "SUM"
];
// --- 1. 初始化: 加载表结构 ---
window.onload = function() {
if (window.cefQuery) {
window.cefQuery({
request: JSON.stringify({ type: "initSchema" }),
onSuccess: function(response) {
const res = JSON.parse(response);
if (res.status === "success") {
dbSchema.tables = res.tables || [];
dbSchema.columns = res.columns || {};
// 扁平化所有列名,方便通用提示
let colSet = new Set();
for (let t in dbSchema.columns) {
dbSchema.columns[t].forEach(c => colSet.add(c));
}
dbSchema.allColumns = Array.from(colSet);
console.log("Schema loaded:", dbSchema);
}
},
onFailure: function() {}
});
} else {
// 测试模式数据
dbSchema.tables = ["student", "course"];
dbSchema.columns = { "student": ["id", "name", "age"], "course": ["id", "title"] };
dbSchema.allColumns = ["id", "name", "age", "title"];
}
};
// --- 2. 输入与键盘事件 ---
input.addEventListener('keydown', function(e) {
// 如果补全框打开,接管按键
if (suggestionBox.style.display === 'block') {
if (handleSuggestionKeys(e)) return;
}
if (e.key === 'Enter') {
handleEnterKey();
}
else if (e.key === 'ArrowUp') {
handleHistory(true, e);
}
else if (e.key === 'ArrowDown') {
handleHistory(false, e);
}
});
// --- 3. 多行命令处理逻辑 (核心升级) ---
function handleEnterKey() {
const rawLine = input.value;
// 1. 如果输入为空,只是换行
if (!rawLine.trim() && !commandBuffer.trim()) {
input.value = '';
return;
}
// 2. 显示当前行
const promptText = isMultiLineMode ? " -> " : "SQL> ";
appendLine(promptText + rawLine);
// 3. 处理历史记录 (每次回车都存,方便找回片段)
if (rawLine.trim()) {
history.push(rawLine);
historyIndex = history.length;
}
// 4. 判断特殊前端命令
const upperLine = rawLine.trim().toUpperCase();
if (upperLine === 'CLS' || upperLine === 'CLEAR') {
outputArea.innerHTML = '';
resetInput();
return;
}
if (upperLine === 'EXIT') {
// 这里可以调用关闭窗口
appendLine("Bye.");
return;
}
// 5. 拼接到缓冲区
commandBuffer += rawLine + " "; // 加空格防止粘连
// 6. 检查是否以分号结尾 (真正的执行触发器)
if (rawLine.trim().endsWith(';')) {
// 移除分号并执行
const finalCmd = commandBuffer.trim().replace(/;$/, '');
processCommand(finalCmd);
resetInput(); // 重置为单行模式
} else {
// 进入多行模式
isMultiLineMode = true;
promptSpan.textContent = " -> ";
input.value = '';
// 滚动到底部
window.scrollTo(0, document.body.scrollHeight);
}
}
function resetInput() {
commandBuffer = "";
isMultiLineMode = false;
promptSpan.textContent = "SQL> ";
input.value = '';
window.scrollTo(0, document.body.scrollHeight);
}
function handleHistory(isUp, e) {
e.preventDefault();
if (isUp && historyIndex > 0) {
historyIndex--;
input.value = history[historyIndex];
} else if (!isUp && historyIndex < history.length - 1) {
historyIndex++;
input.value = history[historyIndex];
} else if (!isUp) {
historyIndex = history.length;
input.value = '';
}
}
// --- 4. 智能感知自动补全 (Context Aware) ---
let currentSuggestions = [];
let activeIndex = -1;
input.addEventListener('input', function() {
const val = this.value;
const cursorPos = this.selectionStart;
// 获取正在输入的单词
const wordInfo = getCurrentWord(val, cursorPos);
if (!wordInfo) {
hideSuggestions();
return;
}
// 分析上下文 (Context Analysis)
const contextType = analyzeContext(val, wordInfo.startPos);
// 获取建议列表
const list = getFilteredSuggestions(wordInfo.word, contextType);
if (list.length > 0) {
showSuggestions(list, wordInfo.startPos);
} else {
hideSuggestions();
}
});
/**
* 简单的上下文分析器
* 返回: 'TABLE' | 'COLUMN' | 'KEYWORD' (默认)
*/
function analyzeContext(text, cursorIndex) {
// 取光标前的文本,转大写,分割成 Token
const prefix = text.substring(0, cursorIndex).toUpperCase();
const tokens = prefix.split(/\s+/).filter(t => t.length > 0);
if (tokens.length === 0) return 'KEYWORD';
const lastToken = tokens[tokens.length - 1];
// 如果上一个词是这些,通常后面接表名
const tableTriggers = ["FROM", "JOIN", "UPDATE", "INTO", "TABLE"];
if (tableTriggers.includes(lastToken)) return 'TABLE';
// 如果上一个词是这些,通常后面接列名
const columnTriggers = ["SELECT", "WHERE", "ORDER", "BY", "GROUP", "HAVING", "SET", "ON", "AND", "OR"];
if (columnTriggers.includes(lastToken) || lastToken.endsWith(',')) return 'COLUMN';
return 'KEYWORD';
}
function getFilteredSuggestions(query, type) {
const q = query.toUpperCase();
let results = [];
// 1. 优先根据上下文添加
if (type === 'TABLE') {
dbSchema.tables.forEach(t => {
if (t.toUpperCase().startsWith(q)) results.push({ text: t, type: 't', detail: 'Table' });
});
} else if (type === 'COLUMN') {
dbSchema.allColumns.forEach(c => {
if (c.toUpperCase().startsWith(q)) results.push({ text: c, type: 'c', detail: 'Column' });
});
// 列名场景下也可能输入表名(例如 table.col
dbSchema.tables.forEach(t => {
if (t.toUpperCase().startsWith(q)) results.push({ text: t, type: 't', detail: 'Table' });
});
}
// 2. 总是添加匹配的关键字 (但在特定上下文排在后面)
keywords.forEach(k => {
if (k.startsWith(q)) results.push({ text: k, type: 'k', detail: 'Keyword' });
});
return results.slice(0, 15); // 限制显示数量
}
// --- 补全 UI 渲染逻辑 --- (复用之前的基础,增加了类型判断)
function showSuggestions(list, wordStartPos) {
currentSuggestions = list;
activeIndex = 0;
// 计算 UI 位置
const promptWidth = getTextWidth(promptSpan.textContent, "bold 16px Consolas");
const inputBefore = input.value.substring(0, wordStartPos);
const inputWidth = getTextWidth(inputBefore, "16px Consolas");
suggestionBox.style.left = (promptWidth + inputWidth) + "px";
suggestionBox.style.top = "26px";
suggestionBox.style.display = "block";
renderList();
}
function renderList() {
suggestionBox.innerHTML = '';
currentSuggestions.forEach((item, idx) => {
const div = document.createElement('div');
div.className = 'suggestion-item' + (idx === activeIndex ? ' active' : '');
let iconChar = 'K';
if (item.type === 't') iconChar = 'T';
if (item.type === 'c') iconChar = 'C';
div.innerHTML = `
<span class="icon icon-${item.type}">${iconChar}</span>
<span class="text">${item.text}</span>
<span class="detail-text">${item.detail}</span>
`;
div.onmousedown = (e) => { e.preventDefault(); applySuggestion(idx); }; // prevent blur
suggestionBox.appendChild(div);
});
// 自动滚动
const activeEl = suggestionBox.children[activeIndex];
if (activeEl) {
const boxTop = suggestionBox.scrollTop;
const boxBottom = boxTop + suggestionBox.clientHeight;
const elTop = activeEl.offsetTop;
const elBottom = elTop + activeEl.clientHeight;
if (elTop < boxTop) suggestionBox.scrollTop = elTop;
if (elBottom > boxBottom) suggestionBox.scrollTop = elBottom - suggestionBox.clientHeight;
}
}
function handleSuggestionKeys(e) {
if (e.key === 'ArrowDown') {
e.preventDefault();
activeIndex = (activeIndex + 1) % currentSuggestions.length;
renderList();
return true;
}
if (e.key === 'ArrowUp') {
e.preventDefault();
activeIndex = (activeIndex - 1 + currentSuggestions.length) % currentSuggestions.length;
renderList();
return true;
}
if (e.key === 'Tab' || e.key === 'Enter') {
e.preventDefault();
applySuggestion(activeIndex);
return true;
}
if (e.key === 'Escape') {
hideSuggestions();
return true;
}
return false;
}
function applySuggestion(index) {
const item = currentSuggestions[index];
const val = input.value;
const wordInfo = getCurrentWord(val, input.selectionStart);
if (wordInfo) {
const prefix = val.substring(0, wordInfo.startPos);
const suffix = val.substring(input.selectionStart);
const newText = prefix + item.text + " " + suffix; // 自动加个空格
input.value = newText;
const newPos = prefix.length + item.text.length + 1;
input.setSelectionRange(newPos, newPos);
}
hideSuggestions();
}
function hideSuggestions() {
suggestionBox.style.display = 'none';
activeIndex = -1;
}
// --- 工具函数 ---
function getCurrentWord(text, cursorIndex) {
const left = text.substring(0, cursorIndex);
const match = left.match(/([a-zA-Z0-9_]+)$/);
if (match) {
return { word: match[1], startPos: match.index };
}
return null;
}
function getTextWidth(text, font) {
const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
const ctx = canvas.getContext("2d");
ctx.font = font;
return ctx.measureText(text).width;
}
// --- 后端通信与显示 (复用) ---
function processCommand(cmd) {
if (window.cefQuery) {
window.cefQuery({
request: JSON.stringify({ type: "executeCommand", command: cmd }),
onSuccess: (res) => renderResponse(JSON.parse(res)),
onFailure: (c, msg) => appendLine("Error: " + msg, true)
});
}
}
function renderResponse(res) {
if (res.status === 'error') {
appendLine(res.output, true);
} else if (res.type === 'table') {
appendLine(generateAsciiTable(res.headers, res.rows));
if (res.info) appendLine(res.info);
} else {
appendLine(res.output);
}
appendLine(""); // 空行分割
}
function appendLine(text, isError = false) {
const div = document.createElement('div');
div.textContent = text;
if (isError) div.className = 'error-text';
outputArea.appendChild(div);
}
function generateAsciiTable(headers, rows) {
if (!headers || !headers.length) return "";
const colWidths = headers.map(h => h.length);
rows.forEach(row => row.forEach((c, i) => colWidths[i] = Math.max(colWidths[i], String(c).length)));
const border = "+" + colWidths.map(w => "-".repeat(w + 2)).join("+") + "+";
const makeRow = (arr) => "|" + arr.map((c, i) => " " + String(c).padEnd(colWidths[i]) + " ").join("|") + "|";
return [border, makeRow(headers), border, ...rows.map(makeRow), border].join("\n");
}
// 点击聚焦 (不影响选中文本)
document.addEventListener('click', (e) => {
if (!window.getSelection().toString() && !suggestionBox.contains(e.target)) input.focus();
});
</script>
</body>
</html>