feat: tighten mobile-first workbench chrome
This commit is contained in:
@@ -778,6 +778,7 @@ function ensureAuthUi() {
|
||||
</div>
|
||||
<div class="helper-text" data-role="auth-message"></div>
|
||||
<div class="auth-actions">
|
||||
<button class="btn btn-secondary" type="button" data-action="logout-session">退出工作区</button>
|
||||
<button class="btn btn-secondary" type="button" data-action="auth-refresh">重新加载</button>
|
||||
<button class="btn btn-primary" type="submit" data-action="submit-auth">自动连接</button>
|
||||
</div>
|
||||
@@ -792,11 +793,13 @@ function renderAuthUi() {
|
||||
ensureAuthUi();
|
||||
const session = appState.session;
|
||||
const openButton = document.querySelector('[data-action="open-auth"]');
|
||||
const logoutButton = document.querySelector('[data-action="logout-session"]');
|
||||
const logoutButtons = document.querySelectorAll('[data-action="logout-session"]');
|
||||
const status = document.querySelector(".auth-status");
|
||||
const message = document.querySelector('[data-role="auth-message"]');
|
||||
if (openButton) openButton.textContent = session ? "连接状态" : "自动连接";
|
||||
if (logoutButton) logoutButton.hidden = !session;
|
||||
logoutButtons.forEach((button) => {
|
||||
button.hidden = !session;
|
||||
});
|
||||
if (status) {
|
||||
status.textContent = appState.busy
|
||||
? appState.message || "正在加载..."
|
||||
@@ -5348,7 +5351,7 @@ function renderDiscoveryScreen() {
|
||||
];
|
||||
const activeTab = getActiveDetailTab("discoveryDetailTab", detailTabs);
|
||||
const selectedSummaryHtml = `
|
||||
<div class="hero-card" style="padding:18px;">
|
||||
<div class="hero-card discovery-selected-hero" style="padding:18px;">
|
||||
<div class="entity-cell">
|
||||
<div class="avatar-lg">${escapeHtml(initials(getAccountName(selected) || "SF"))}</div>
|
||||
<div>
|
||||
@@ -6035,7 +6038,7 @@ function renderProductionScreen() {
|
||||
${renderMainAgentLandingNotice("production")}
|
||||
<div class="panel pad">
|
||||
<div class="panel-head"><div><h3>生产队列</h3><div class="panel-subtitle">最近任务的真实状态</div></div></div>
|
||||
<div class="layout-grid grid-4" style="margin-top:16px;">
|
||||
<div class="layout-grid grid-4 production-queue-grid" style="margin-top:16px;">
|
||||
<div class="queue-card"><h4>分析任务</h4><p>最近 ${escapeHtml(formatNumber(jobs.filter((item) => item.line_type === "analysis").length))} 条</p></div>
|
||||
<div class="queue-card"><h4>实拍剪辑</h4><p>最近 ${escapeHtml(formatNumber(jobs.filter((item) => item.line_type === "real_cut").length))} 条</p></div>
|
||||
<div class="queue-card"><h4>AI 视频</h4><p>最近 ${escapeHtml(formatNumber(jobs.filter((item) => item.line_type === "ai_video").length))} 条</p></div>
|
||||
|
||||
@@ -1930,6 +1930,7 @@ tbody tr:hover {
|
||||
}
|
||||
|
||||
.topbar {
|
||||
display: none;
|
||||
margin-top: 6px;
|
||||
padding: 14px;
|
||||
border-radius: 18px;
|
||||
@@ -2193,6 +2194,11 @@ tbody tr:hover {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.discovery-selected-hero .mini-grid,
|
||||
.production-queue-grid {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-flow-focus-card {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
@@ -2263,7 +2269,16 @@ tbody tr:hover {
|
||||
.oneliner-fab {
|
||||
right: 14px;
|
||||
bottom: calc(96px + env(safe-area-inset-bottom));
|
||||
padding: 11px 12px;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
justify-content: center;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.oneliner-fab-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table {
|
||||
|
||||
@@ -30,6 +30,7 @@ test("mobile shell includes a native-like header, drawer toggle, and bottom tab
|
||||
assert.match(HTML, /data-action="open-mobile-sidebar"/);
|
||||
assert.match(HTML, /class="mobile-tabbar"/);
|
||||
assert.match(HTML, /class="mobile-sidebar-backdrop"/);
|
||||
assert.match(HTML, /class="mobile-workspace-summary"/);
|
||||
assert.match(HTML, /data-screen-target="dashboard"[\s\S]*mobile-tabbar-item/);
|
||||
assert.match(HTML, /data-screen-target="intake"[\s\S]*mobile-tabbar-item/);
|
||||
assert.match(HTML, /data-screen-target="discovery"[\s\S]*mobile-tabbar-item/);
|
||||
@@ -45,6 +46,10 @@ test("mobile shell styling uses safe-area padding, drawer navigation, and fixed
|
||||
assert.match(CSS, /\.mobile-sidebar-open\s+\.sidebar\s*\{[\s\S]*transform:\s*translateX\(0\)/);
|
||||
assert.match(CSS, /\.content\s*\{[\s\S]*padding-bottom:\s*calc\(110px \+ env\(safe-area-inset-bottom\)\)/);
|
||||
assert.match(CSS, /\.oneliner-fab\s*\{[\s\S]*bottom:\s*calc\(96px \+ env\(safe-area-inset-bottom\)\)/);
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.mobile-workspace-summary\s*\{[\s\S]*display:\s*flex/);
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.workspace-switch span\s*\{[\s\S]*display:\s*none/);
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.auth-status\s*\{[\s\S]*display:\s*none/);
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.oneliner-fab-text\s*\{[\s\S]*display:\s*none/);
|
||||
});
|
||||
|
||||
test("mobile shell javascript syncs drawer state and active labels with the current screen", () => {
|
||||
@@ -54,6 +59,7 @@ test("mobile shell javascript syncs drawer state and active labels with the curr
|
||||
assert.match(APP, /function setMobileSidebarOpen\(next\)/);
|
||||
assert.match(APP, /function getScreenLabel\(screenId = appState\.screen\)/);
|
||||
assert.match(APP, /function syncMobileShell\(\)/);
|
||||
assert.match(APP, /const mobileWorkspaceSummary = document\.querySelector\("\.mobile-workspace-summary"\)/);
|
||||
assert.match(shell, /syncMobileShell\(\);/);
|
||||
assert.match(APP, /setMobileSidebarOpen\(false\);[\s\S]*appState\.screen = resolvedId;/);
|
||||
assert.match(clicks, /name === "open-mobile-sidebar"/);
|
||||
@@ -69,6 +75,13 @@ test("mobile layout turns screen actions and page tabs into native-like horizont
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.page-detail-tabs \.tab\s*\{[\s\S]*flex:\s*0 0 auto/);
|
||||
});
|
||||
|
||||
test("mobile shell removes duplicated desktop topbar and collapses the main agent fab", () => {
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.topbar\s*\{[\s\S]*display:\s*none/);
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.oneliner-fab\s*\{[\s\S]*width:\s*52px/);
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.oneliner-fab\s*\{[\s\S]*justify-content:\s*center/);
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.oneliner-fab-text\s*\{[\s\S]*display:\s*none/);
|
||||
});
|
||||
|
||||
test("detail tab buttons expose the active state for touch navigation", () => {
|
||||
const detailTabs = extractBetween(APP, "function renderDetailTabs(stateKey, tabs) {", "function renderDiscoveryOverviewSection(");
|
||||
assert.match(detailTabs, /aria-pressed="\$\{tab\.value === active \? "true" : "false"\}"/);
|
||||
@@ -150,6 +163,17 @@ test("discovery and production screens expose mobile focus cards with next-step
|
||||
assert.match(cssMobile, /\.mobile-flow-focus-card/);
|
||||
});
|
||||
|
||||
test("mobile heavy screens mark redundant desktop metric blocks for compact hiding", () => {
|
||||
const discovery = extractBetween(APP, "function renderDiscoveryScreen()", "function renderTrackingScreen()");
|
||||
const production = extractBetween(APP, "function renderProductionScreen()", "function renderReviewScreen()");
|
||||
const cssMobile = extractBetween(CSS, "@media (max-width: 760px) {", "@media (max-width: 560px) {");
|
||||
|
||||
assert.match(discovery, /discovery-selected-hero/);
|
||||
assert.match(production, /production-queue-grid/);
|
||||
assert.match(cssMobile, /\.discovery-selected-hero \.mini-grid/);
|
||||
assert.match(cssMobile, /\.production-queue-grid/);
|
||||
});
|
||||
|
||||
test("projects screen uses an adaptive project grid instead of a fixed three-column squeeze", () => {
|
||||
const projects = extractBetween(APP, "function renderProjectsScreen()", "function getActiveDetailTab(");
|
||||
assert.match(projects, /project-status-grid/);
|
||||
|
||||
Reference in New Issue
Block a user