Skip to content
Closed
5 changes: 5 additions & 0 deletions media/console/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
state.schemas = message.schemas || state.schemas;
renderTasks(message.tasks || []);
Console.refreshDrawer();
Console.renderHealth();
// 多账户存储概要随 tasks 消息一起到达(probe 探测结果);store 数据保持独立状态
Console.renderMultiAccount(message.multiAccount || state.multiAccount, state.accountStore);
break;
Expand All @@ -31,13 +32,15 @@
Console.renderMultiAccount(message.multiAccount || state.multiAccount, state.accountStore);
renderTasks(state.currentTasks);
Console.refreshDrawer();
Console.renderHealth();
break;
case 'taskConfigs':
state.taskConfigs = message.configs || {};
// UI 折叠状态(启动设置区/配置分组/卡片)随任务配置一起下发,重开面板复用
if (message.uiState) state.uiState = message.uiState;
renderTasks(state.currentTasks);
Console.refreshDrawer();
Console.renderHealth();
break;
// 配置接管:全局配置组 + 快照(配置分段数据源)
case 'globalGroups':
Expand All @@ -55,6 +58,8 @@
};
renderTasks(state.currentTasks);
Console.refreshDrawer();
Console.renderHealth();
Console.renderHealth();
} else if (message.target === 'global') {
state.globalSnapshots[message.name] = message.values || {};
Console.renderConfig(state.globalGroups, state.globalSnapshots, state.expandedGlobalGroups || []);
Expand Down
193 changes: 193 additions & 0 deletions media/console/console.css
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,199 @@ button.secondary:hover:not(:disabled) { background: var(--vscode-button-secondar
.config-section-title--toggle { cursor: pointer; user-select: none; }
.config-section-title__chev { display: inline-block; width: 11px; margin-right: 2px; color: var(--muted); }

/* ═══ 主从双栏 shell(侧栏导航 + 主内容) ═════════════════════ */
.shell { display: flex; gap: 10px; align-items: stretch; }
.lay-side {
position: sticky;
top: 0;
align-self: flex-start;
display: flex;
flex-direction: column;
gap: 2px;
width: 158px;
flex: 0 0 auto;
max-height: 100vh;
padding: 6px 6px 6px 0;
border-right: 1px solid var(--border);
background: var(--vscode-editor-background);
}
.lay-side__item {
position: relative;
display: flex;
align-items: center;
gap: 8px;
padding: 7px 12px;
border-radius: var(--radius-sm);
color: var(--muted);
font-size: 13px;
cursor: pointer;
user-select: none;
}
.lay-side__item:hover { background: var(--surface-hover); color: var(--vscode-editor-foreground); }
.lay-side__item.is-active {
background: var(--surface-hover);
color: var(--vscode-editor-foreground);
box-shadow: inset 2px 0 0 var(--border-strong);
}
.lay-side__ico {
width: 14px; height: 14px;
flex: 0 0 auto;
border-radius: 4px;
background: var(--border-strong);
opacity: .7;
}
.lay-side__item.is-active .lay-side__ico { opacity: 1; }
.lay-side__foot { margin-top: auto; padding: 10px 12px; font-size: 11px; color: var(--muted); }
.lay-main { flex: 1; min-width: 0; }

/* ═══ 配置健康度条(任务分段顶部,真实快照数据) ══════════════ */
.rc-health {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 18px;
margin: 0 0 10px;
padding: 9px 12px;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--surface);
}
.rc-health__head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; flex: 1; min-width: 0; }
.rc-health__title { font-size: 12px; font-weight: 700; }
.rc-health__cells { display: flex; gap: 8px; flex: 1; min-width: 0; }
.rc-cell { flex: 1; min-width: 96px; padding: 6px 12px; border-radius: var(--radius-sm); background: var(--surface-hover); }
.rc-cell__label { font-size: 10px; color: var(--muted); }
.rc-cell__value { font-size: 16px; font-weight: 700; }
.rc-cell.is-ok .rc-cell__value { color: var(--ok); }
.rc-cell.is-warn .rc-cell__value { color: var(--warn); }

/* ═══ 执行队列条(一次性队列可视化) ══════════════════════════ */
.rc-queue {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 12px;
margin: 0 0 10px;
padding: 8px 12px;
border: 1px dashed var(--border-strong);
border-radius: var(--radius-md);
background: var(--surface);
}
.rc-queue__head { display: flex; align-items: baseline; gap: 8px; }
.rc-queue__head b { font-size: 12px; }
.rc-queue__chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; flex: 1; min-width: 0; }
.rc-queue__chip {
padding: 3px 10px;
border: 1px solid var(--border);
border-radius: 999px;
background: var(--surface-hover);
font-size: 11px;
}

/* ═══ 运行状态灯(任务卡标题行) ══════════════════════════════ */
.rc-dot {
flex: 0 0 auto;
width: 8px; height: 8px;
border-radius: 50%;
background: color-mix(in srgb, var(--muted) 55%, transparent);
}
.task-card[data-run="running"] .rc-dot,
.task-card[data-run="polling"] .rc-dot {
background: var(--run);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--run) 18%, transparent);
animation: gbar-pulse 1.8s ease-in-out infinite;
}
.task-card[data-run="queued"] .rc-dot,
.task-card[data-run="enqueued"] .rc-dot { background: var(--warn); }
.task-card[data-run="armed"] .rc-dot { background: var(--ok); opacity: .75; }

/* ═══ 任务卡悬停弹出:body 级固定定位(滚动容器裁剪免疫 · 毛玻璃 · 只读) ═══ */
.task-card--pop:hover { border-color: color-mix(in srgb, var(--run) 40%, var(--border-strong)); }

.gpop {
position: fixed;
z-index: 9999;
width: 250px;
max-height: min(58vh, 420px);
overflow-y: auto;
padding: 10px 12px;
border: 1px solid var(--border-strong);
border-radius: 10px;
background: color-mix(in srgb, var(--surface-raised, var(--surface)) 88%, transparent);
backdrop-filter: blur(18px) saturate(1.5);
-webkit-backdrop-filter: blur(18px) saturate(1.5);
box-shadow: 0 14px 38px color-mix(in srgb, #000 40%, transparent);
opacity: 0;
visibility: hidden;
transform: translateY(4px) scale(.98);
pointer-events: none;
transition: opacity .16s ease, transform .16s cubic-bezier(.2,.9,.3,1.2), visibility 0s linear .16s;
}
.gpop.is-visible {
opacity: 1;
visibility: visible;
transform: none;
pointer-events: auto; /* 内容超高时允许内部滚动 */
transition: opacity .16s ease, transform .16s cubic-bezier(.2,.9,.3,1.2), visibility 0s;
}
/* 弹层内部滚动条(内容超高时全量翻阅) */
.gpop::-webkit-scrollbar { width: 8px; }
.gpop::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--border-strong) 80%, transparent);
border-radius: 4px;
}
.gpop::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }
.gpop::-webkit-scrollbar-track { background: transparent; }
@keyframes popfade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.gpop .pop-title {
display: flex; align-items: baseline; justify-content: space-between;
margin-bottom: 2px; font-size: 12px; font-weight: 700;
animation: popfade .24s ease both;
}
.gpop .pop-sub { font-size: 10px; font-weight: 400; color: var(--muted); }
.gpop .grp { margin: 8px 0; animation: popfade .24s ease both; }
.gpop .grp-head {
display: flex; align-items: center; justify-content: space-between;
padding: 0 2px 2px;
font-size: 10px; letter-spacing: .08em; color: var(--muted);
}
.gpop .grp-head b { font-size: 12px; font-weight: 600; letter-spacing: 0; color: var(--vscode-editor-foreground); }
.gpop .grp-head .cnt {
padding: 1px 8px; border-radius: 999px;
background: color-mix(in srgb, var(--run) 14%, transparent);
color: var(--run); font-size: 10px;
}
.gpop .grp-body { position: relative; margin: 2px 0 0 8px; padding-left: 12px; }
.gpop .grp-body::before {
content: '';
position: absolute;
top: 2px; bottom: 6px; left: 0;
width: 2px; border-radius: 1px;
background: linear-gradient(180deg, var(--border-strong), transparent);
}
.gpop .it {
position: relative;
display: flex; align-items: center;
margin: 4px 0; padding: 4px 9px;
border-left: 2px solid var(--border-strong);
border-radius: 6px;
background: color-mix(in srgb, var(--surface-hover) 72%, transparent);
font-size: 12px; overflow: hidden;
}
.gpop .it::before {
content: '';
position: absolute;
top: 50%; left: -12px;
width: 10px; height: 1px;
background: var(--border-strong); opacity: .7;
}
.gpop .it > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 分段切换内容淡入重放(弹出层规范配套:切 Tab 后 1.2s 抑制弹出 + 内容重放淡入) */
.page.is-entering { animation: pagefade .26s ease both; }
@keyframes pagefade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }



/* hidden 属性兜底:作者样式里的 display(.config-fields 的 grid、卡片 body 的 flex 等)
会覆盖 UA 的 [hidden]{display:none},导致所有基于 hidden 的折叠/显隐在真机上失效
Expand Down
93 changes: 79 additions & 14 deletions media/console/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,24 @@
// ── 分段切换 ─────────────────────────────────────────────────────────

function switchSeg(seg) {
for (const button of document.querySelectorAll('.seg__btn')) {
const active = button.dataset.seg === seg;
button.classList.toggle('is-active', active);
button.setAttribute('aria-selected', active ? 'true' : 'false');
// 弹出层规范:切 Tab 后 1.2s 内抑制悬停弹出 + 目标页内容淡入重放
globalThis.TaskLauncherTaskCard?.suppressPopups?.();
for (const item of document.querySelectorAll('.lay-side__item')) {
const active = item.dataset.seg === seg;
item.classList.toggle('is-active', active);
item.setAttribute('aria-selected', active ? 'true' : 'false');
}
$('pageTasks').hidden = seg !== 'tasks';
$('pageGame').hidden = seg !== 'game';
$('pageConfig').hidden = seg !== 'config';
$('pageAccounts').hidden = seg !== 'accounts'; }
$('pageAccounts').hidden = seg !== 'accounts';
const target = document.getElementById(`page${seg.charAt(0).toUpperCase()}${seg.slice(1)}`);
if (target) {
target.classList.remove('is-entering');
void target.offsetWidth; // 强制 reflow,保证动画每次都重放
target.classList.add('is-entering');
}
}

// ── 配置分段:全局配置组卡片(复用 configPanel,伪 task = __global__::组名) ──

Expand Down Expand Up @@ -55,11 +64,56 @@
empty.className = 'config-empty';
empty.textContent = t('noConfigParameters');
host.appendChild(empty);
renderHealth();
return;
}
for (const group of state.globalGroups) {
host.appendChild(buildGlobalCard(group, state.expandedGlobalGroups.includes(group.name)));
}
renderHealth();
}

/** 配置健康度条:快照组 / 快照字段 / 已定制任务(全部来自既有真实数据) */
function renderHealth() {
const strip = $('healthStrip');
if (!strip) return;
const groups = state.globalGroups || [];
if (!groups.length) {
strip.hidden = true;
strip.textContent = '';
return;
}
let fieldCount = 0;
for (const group of groups) fieldCount += (group.fields || []).length;
const owned = document.querySelectorAll('[data-role="config-toggle"].has-overrides').length;
strip.textContent = '';
const head = document.createElement('div');
head.className = 'rc-health__head';
const title = document.createElement('span');
title.className = 'rc-health__title';
title.textContent = t('healthTitle');
const cells = document.createElement('div');
cells.className = 'rc-health__cells';
const defs = [
['healthGroups', groups.length, 'ok'],
['healthFields', fieldCount, 'ok'],
['healthOwned', owned, owned > 0 ? 'warn' : 'ok'],
];
for (const [key, value, tone] of defs) {
const cell = document.createElement('div');
cell.className = `rc-cell is-${tone}`;
const label = document.createElement('div');
label.className = 'rc-cell__label';
label.textContent = t(key);
const num = document.createElement('div');
num.className = 'rc-cell__value';
num.textContent = String(value);
cell.append(label, num);
cells.appendChild(cell);
}
head.append(title, cells);
strip.append(head);
strip.hidden = false;
}

function buildGlobalCard(group, expanded) {
Expand Down Expand Up @@ -638,15 +692,25 @@
function init() {
document.title = t('consoleTitle');

// 分段导航
$('segTasks').textContent = t('consoleTabTasks');
$('segGame').textContent = t('consoleTabGame');
$('segConfig').textContent = t('consoleTabConfig');
$('segAccounts').textContent = t('consoleTabAccounts');
$('segTasks').addEventListener('click', () => switchSeg('tasks'));
$('segGame').addEventListener('click', () => switchSeg('game'));
$('segConfig').addEventListener('click', () => switchSeg('config'));
$('segAccounts').addEventListener('click', () => switchSeg('accounts'));
// 侧栏导航(主从双栏):文案 + 点击/键盘切换
const sideTabs = [
['segTasks', 'consoleTabTasks'],
['segGame', 'consoleTabGame'],
['segConfig', 'consoleTabConfig'],
['segAccounts', 'consoleTabAccounts'],
];
for (const [id, key] of sideTabs) {
const item = $(id);
item.querySelector('.lay-side__label').textContent = t(key);
item.addEventListener('click', () => switchSeg(item.dataset.seg));
item.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
switchSeg(item.dataset.seg);
}
});
}
$('sideFoot').textContent = t('consoleTitle');

// 任务分段
const search = $('taskSearch');
Expand Down Expand Up @@ -787,6 +851,7 @@
closeDrawer,
refreshDrawer,
renderConfig,
renderHealth,
renderMultiAccount,
};
})();
Loading
Loading