fix: update setting board in message turnnel page
This commit is contained in:
1 parent
3fcf12e707
commit
7ce4778903
4 files changed
+124
-10
No files matched your search
@@ -155,6 +155,8 @@ const gaServiceStore = {
|
||||
return http(`/services/logs?id=${encodeURIComponent(id)}&tail=${encodeURIComponent(tail)}`);
|
||||
}
|
||||
case 'services/panel': return http('/services/panel');
|
||||
case 'services/mykey/get': return http('/services/mykey');
|
||||
case 'services/mykey/save': return http('/services/mykey', { method: 'POST', body: params || {} });
|
||||
case 'app/path/selectGaRoot': return http('/config');
|
||||
case 'list_continuable_sessions': return { sessions: [] };
|
||||
case 'restore_session': throw new Error('restore_session is not implemented in web2 bridge');
|
||||
@@ -194,6 +196,8 @@ const gaServiceStore = {
|
||||
stopService,
|
||||
getServiceLogs: (id, tail = 200) => rpc('services/logs', { id, tail }),
|
||||
getServicePanel: () => rpc('services/panel', {}),
|
||||
getMykeyContent: () => rpc('services/mykey/get', {}),
|
||||
saveMykeyContent: (content) => rpc('services/mykey/save', { content }),
|
||||
pollSession: (sessionId, afterId = 0) => rpc('session/poll', { sessionId, afterId }),
|
||||
rpc,
|
||||
onBridgeMessage: (cb) => on('bridge-message', cb),
|
||||
@@ -254,6 +258,8 @@ const I18N = {
|
||||
'err.channelNotConfigured': '请先在 mykey.py 中配置该平台',
|
||||
'sys.channelStarted': '已启动', 'sys.channelStopped': '已停止',
|
||||
'modal.channelLogs': '进程日志',
|
||||
'modal.mykeyConfig': 'mykey.py 配置',
|
||||
'sys.configSaved': '配置已保存',
|
||||
'st.starting': '启动中…', 'st.stopping': '停止中…',
|
||||
'st.online': '在线', 'st.offline': '离线', 'st.error': '错误', 'st.running': '运行', 'st.abnormal': '异常',
|
||||
'act.configure': '配置', 'act.logs': '日志', 'act.restart': '重启', 'act.stop': '停止', 'act.start': '启动',
|
||||
@@ -310,6 +316,8 @@ const I18N = {
|
||||
'err.channelNotConfigured': 'Configure this platform in mykey.py first',
|
||||
'sys.channelStarted': 'Started', 'sys.channelStopped': 'Stopped',
|
||||
'modal.channelLogs': 'Process logs',
|
||||
'modal.mykeyConfig': 'mykey.py',
|
||||
'sys.configSaved': 'Configuration saved',
|
||||
'st.starting': 'Starting…', 'st.stopping': 'Stopping…',
|
||||
'st.online': 'Online', 'st.offline': 'Offline', 'st.error': 'Error', 'st.running': 'Running', 'st.abnormal': 'Error',
|
||||
'act.configure': 'Configure', 'act.logs': 'Logs', 'act.restart': 'Restart', 'act.stop': 'Stop', 'act.start': 'Start',
|
||||
@@ -960,6 +968,10 @@ const chanEmptyEl = document.getElementById('chan-empty');
|
||||
const chanLogModal = document.getElementById('chan-log-modal');
|
||||
const chanLogPre = document.getElementById('chan-log-pre');
|
||||
const chanLogTitle = document.getElementById('chan-log-title');
|
||||
const chanConfigModal = document.getElementById('chan-config-modal');
|
||||
const chanConfigTitle = document.getElementById('chan-config-title');
|
||||
const chanConfigEditor = document.getElementById('chan-config-editor');
|
||||
const chanConfigSave = document.getElementById('chan-config-save');
|
||||
let _chanLogId = null;
|
||||
let _chanBusy = false;
|
||||
let _chanToastTimer = null;
|
||||
@@ -1103,13 +1115,43 @@ async function openChannelLogs(id) {
|
||||
chanLogPre.textContent = t('err.channelLoad') + ': ' + (e.message || e);
|
||||
}
|
||||
}
|
||||
async function openChannelMykey() {
|
||||
async function openChannelMykey(channelId) {
|
||||
if (!chanConfigModal || !chanConfigEditor) return;
|
||||
const ch = gaServiceStore.get(channelId) || { id: channelId };
|
||||
if (chanConfigTitle) {
|
||||
chanConfigTitle.textContent = t('modal.mykeyConfig') + (channelId ? ' · ' + channelDisplayName(ch) : '');
|
||||
}
|
||||
chanConfigEditor.value = t('ch.loading');
|
||||
chanConfigEditor.disabled = true;
|
||||
if (chanConfigSave) chanConfigSave.disabled = true;
|
||||
openModal('chan-config-modal');
|
||||
try {
|
||||
await window.ga.openMykey();
|
||||
const res = await window.ga.getMykeyContent();
|
||||
chanConfigEditor.value = res.content || '';
|
||||
} catch (e) {
|
||||
chanConfigEditor.value = t('err.channelLoad') + ': ' + (e.message || e);
|
||||
} finally {
|
||||
chanConfigEditor.disabled = false;
|
||||
if (chanConfigSave) chanConfigSave.disabled = false;
|
||||
chanConfigEditor.focus();
|
||||
}
|
||||
}
|
||||
async function saveChannelMykey() {
|
||||
if (!chanConfigEditor || !chanConfigSave) return;
|
||||
chanConfigSave.disabled = true;
|
||||
try {
|
||||
await window.ga.saveMykeyContent(chanConfigEditor.value);
|
||||
showChanToast(t('sys.configSaved'), '', 'ok');
|
||||
chanConfigModal.hidden = true;
|
||||
} catch (e) {
|
||||
showChanToast(t('err.channelLoad'), e.message || String(e), 'err');
|
||||
} finally {
|
||||
chanConfigSave.disabled = false;
|
||||
}
|
||||
}
|
||||
if (chanConfigSave) {
|
||||
chanConfigSave.addEventListener('click', saveChannelMykey);
|
||||
}
|
||||
|
||||
/* ═══════════════ 状态面板(复用 ServiceManager + 启停/日志) ═══════════════ */
|
||||
const statusListEl = document.getElementById('status-list');
|
||||
@@ -1228,7 +1270,7 @@ if (chanListEl) {
|
||||
return;
|
||||
}
|
||||
if (act === 'configure') {
|
||||
openChannelMykey();
|
||||
openChannelMykey(id);
|
||||
return;
|
||||
}
|
||||
if (act === 'toggle') {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>GenericAgent</title>
|
||||
<link rel="stylesheet" href="styles.css?v=4">
|
||||
<link rel="stylesheet" href="styles.css?v=5">
|
||||
<!-- DEV 脚手架(标注模式):删本行 + 下方 annot.js 一行即可彻底移除 -->
|
||||
<link rel="stylesheet" href="annot.css">
|
||||
</head>
|
||||
@@ -297,19 +297,38 @@
|
||||
<!-- 渠道日志 弹窗 -->
|
||||
<div class="modal" id="chan-log-modal" hidden>
|
||||
<div class="modal-backdrop" data-close></div>
|
||||
<div class="modal-card modal-card-wide" role="dialog">
|
||||
<div class="modal-card modal-card-wide modal-card-inset" role="dialog">
|
||||
<div class="modal-head">
|
||||
<div class="modal-title" id="chan-log-title" data-i18n="modal.channelLogs"></div>
|
||||
<button class="modal-x" data-close data-i18n-title="common.close" title="">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body modal-body-inset">
|
||||
<pre class="chan-log-pre" id="chan-log-pre"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- mykey 配置 弹窗 -->
|
||||
<div class="modal" id="chan-config-modal" hidden>
|
||||
<div class="modal-backdrop" data-close></div>
|
||||
<div class="modal-card modal-card-wide modal-card-inset" role="dialog">
|
||||
<div class="modal-head">
|
||||
<div class="modal-title" id="chan-config-title" data-i18n="modal.mykeyConfig"></div>
|
||||
<button class="modal-x" data-close data-i18n-title="common.close" title="">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body modal-body-inset chan-config-body">
|
||||
<textarea id="chan-config-editor" class="mykey-editor" spellcheck="false"></textarea>
|
||||
<div class="chan-config-foot">
|
||||
<button type="button" class="chan-config-save" id="chan-config-save" data-i18n="common.save"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 配置 弹窗 -->
|
||||
<div class="modal" id="settings-modal" hidden>
|
||||
<div class="modal-backdrop" data-close></div>
|
||||
@@ -353,7 +372,7 @@
|
||||
<div id="toast-root" class="toast-root" aria-live="polite"></div>
|
||||
|
||||
<script src="vendor/marked.min.js"></script>
|
||||
<script src="app.js?v=6"></script>
|
||||
<script src="app.js?v=7"></script>
|
||||
<script src="annot.js"></script><!-- DEV 脚手架,可删 -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -344,12 +344,35 @@ body{ background:var(--bg); color:var(--txt); font-size:13px; overflow:hidden; }
|
||||
.toast-detail{ font-size:12px; opacity:.88; display:block; word-break:break-word; }
|
||||
.chan-log-pre{
|
||||
max-height:360px; overflow:auto; margin:0;
|
||||
font-family:ui-monospace,Consolas,monospace; font-size:12px;
|
||||
color:var(--txt-2); background:var(--field-bg);
|
||||
font-family:inherit; font-size:13px; line-height:1.5;
|
||||
color:var(--txt-2); background:#ffffff;
|
||||
border:1px solid var(--line); border-radius:var(--radius-sm);
|
||||
padding:12px; white-space:pre-wrap; word-break:break-word;
|
||||
padding:14px 16px; white-space:pre-wrap; word-break:break-word;
|
||||
}
|
||||
.modal-card-wide{ width:min(720px,92vw); }
|
||||
.modal-card-inset{ width:min(680px,calc(92vw - 48px)); }
|
||||
.modal-body-inset{ padding:0 18px 18px; min-height:0; }
|
||||
.chan-config-body{
|
||||
display:flex; flex-direction:column; gap:8px;
|
||||
}
|
||||
.mykey-editor{
|
||||
flex:1; min-height:420px; width:100%;
|
||||
padding:0; margin:0;
|
||||
font-family:inherit; font-size:13px; line-height:1.5;
|
||||
color:var(--txt); background:#ffffff;
|
||||
border:none; border-radius:0; resize:vertical; outline:none;
|
||||
}
|
||||
.mykey-editor:focus{ outline:none; }
|
||||
.chan-config-foot{
|
||||
display:flex; justify-content:flex-end;
|
||||
}
|
||||
.chan-config-save{
|
||||
border:1px solid var(--line); background:var(--card); color:var(--txt-2);
|
||||
border-radius:6px; padding:5px 12px; font-size:12px; font-weight:600;
|
||||
font-family:inherit; cursor:pointer;
|
||||
}
|
||||
.chan-config-save:hover{ background:var(--hover-2); }
|
||||
.chan-config-save:disabled{ opacity:.45; cursor:not-allowed; }
|
||||
.chan-meta{ display:flex; flex-direction:column; gap:2px; min-width:0; }
|
||||
.chan-meta .chan-name{ font-size:14px; }
|
||||
.chan-meta .chan-path{ font-size:11px; color:var(--muted); }
|
||||
|
||||
@@ -23,6 +23,8 @@ HTTP API:
|
||||
POST /services/stop body: {"id":"frontends/qqapp.py"}
|
||||
GET /services/logs?id=frontends/qqapp.py&tail=200
|
||||
GET /services/panel
|
||||
GET /services/mykey
|
||||
POST /services/mykey body: {"content":"..."}
|
||||
|
||||
WS API (state sync):
|
||||
GET /ws -> on connect sends services.snapshot; service.changed on updates
|
||||
@@ -856,6 +858,32 @@ def _open_path_in_editor(target: Path) -> None:
|
||||
subprocess.Popen(["xdg-open", path])
|
||||
|
||||
|
||||
def _mykey_file() -> Path:
|
||||
root = Path(manager.ga_root)
|
||||
target = root / "mykey.py"
|
||||
if not target.is_file():
|
||||
template = root / "mykey_template.py"
|
||||
if template.is_file():
|
||||
target.write_text(template.read_text(encoding="utf-8"), encoding="utf-8")
|
||||
return target
|
||||
|
||||
|
||||
async def mykey_get_handler(request):
|
||||
target = _mykey_file()
|
||||
content = target.read_text(encoding="utf-8") if target.is_file() else ""
|
||||
return json_ok({"content": content, "path": str(target)})
|
||||
|
||||
|
||||
async def mykey_save_handler(request):
|
||||
data = await read_json(request)
|
||||
content = data.get("content")
|
||||
if content is None:
|
||||
return json_ok({"ok": False, "error": "missing_content"}, status=400)
|
||||
target = _mykey_file()
|
||||
target.write_text(str(content), encoding="utf-8")
|
||||
return json_ok({"ok": True, "path": str(target)})
|
||||
|
||||
|
||||
async def service_start_handler(request):
|
||||
body = await read_json(request)
|
||||
sid = body.get("id") or request.query.get("id")
|
||||
@@ -928,6 +956,8 @@ def create_app():
|
||||
app.router.add_post("/services/stop", service_stop_handler)
|
||||
app.router.add_get("/services/logs", service_logs_handler)
|
||||
app.router.add_get("/services/panel", service_panel_handler)
|
||||
app.router.add_get("/services/mykey", mykey_get_handler)
|
||||
app.router.add_post("/services/mykey", mykey_save_handler)
|
||||
|
||||
# Serve static frontend (desktop/static/)
|
||||
static_dir = APP_DIR / "desktop" / "static"
|
||||
|
||||
Reference in new issue
Block a user