Merge pull request #365 from pikasTech/fix/remove-resource-explorer-352
fix: remove resource explorer from workbench
This commit is contained in:
@@ -219,9 +219,9 @@ test("dedicated layout smoke CLI supports static build and DEV live shorthand",
|
||||
assert.equal(liveExplicit.urlExplicit, true);
|
||||
});
|
||||
|
||||
test("source/default smoke covers #278 sidebar collapse static contract", () => {
|
||||
test("source/default smoke covers #352 resource explorer removal contract", () => {
|
||||
const report = runDevCloudWorkbenchStaticSmoke();
|
||||
assert.equal(report.checks.find((check) => check.id === "feedback-278-sidebar-collapse-contract")?.status, "pass");
|
||||
assert.equal(report.checks.find((check) => check.id === "feedback-352-resource-explorer-removed")?.status, "pass");
|
||||
});
|
||||
|
||||
test("source/default smoke covers #276 two-column wiring long-table contract", () => {
|
||||
@@ -693,7 +693,7 @@ test("Code Agent quick prompt fixture fills input, does not autosend writes, and
|
||||
assert.equal(layout.observations.some((item) => item.viewport.width === 390 && item.layoutOk), true);
|
||||
});
|
||||
|
||||
test("layout smoke verifies desktop and mobile sidebar collapse geometry", async () => {
|
||||
test("layout smoke verifies desktop and mobile default workbench geometry without resource explorer", async () => {
|
||||
const report = await runDevCloudWorkbenchLayoutSmoke();
|
||||
if (report.status === "skip") {
|
||||
assert.match(report.summary, /Playwright is unavailable/u);
|
||||
@@ -718,17 +718,9 @@ test("layout smoke verifies desktop and mobile sidebar collapse geometry", async
|
||||
assert.equal(report.validationCommands.includes(command), true, `missing ${command}`);
|
||||
}
|
||||
for (const id of [
|
||||
"layout-desktop-expanded",
|
||||
"layout-desktop-collapsed",
|
||||
"layout-desktop-restored",
|
||||
"layout-narrow-desktop-expanded",
|
||||
"layout-narrow-desktop-collapsed",
|
||||
"layout-narrow-desktop-restored",
|
||||
"layout-mobile-collapsed",
|
||||
"layout-mobile-drawer",
|
||||
"layout-explorer-resize-desktop",
|
||||
"layout-explorer-resize-narrow-desktop",
|
||||
"layout-explorer-resize-mobile",
|
||||
"layout-desktop-default",
|
||||
"layout-narrow-desktop-default",
|
||||
"layout-mobile-default",
|
||||
"layout-gate-desktop",
|
||||
"layout-gate-narrow-desktop",
|
||||
"layout-gate-mobile"
|
||||
@@ -750,60 +742,29 @@ test("layout smoke verifies desktop and mobile sidebar collapse geometry", async
|
||||
assert.equal(report.checks.find((check) => check.id === "layout-issue-287-future-hardware-status-tabs")?.status, "pass");
|
||||
assert.equal(report.checks.find((check) => check.id === "layout-issue-288-future-single-table-gate")?.status, "skip");
|
||||
|
||||
const desktopCollapsed = report.checks.find((check) => check.id === "layout-desktop-collapsed")?.observations;
|
||||
assert.equal(desktopCollapsed.explorerCollapsed, true);
|
||||
assert.equal(desktopCollapsed.toggle.ariaLabel, "展开左侧资源树");
|
||||
assert.equal(desktopCollapsed.toggle.text, "展开资源树");
|
||||
assert.equal(desktopCollapsed.collapsedWidthGain, true);
|
||||
assert.equal(desktopCollapsed.keyTargetsReachable, true);
|
||||
assert.equal(desktopCollapsed.wiring.longTableOk, true);
|
||||
assert.equal(desktopCollapsed.wiring.noHorizontalScroll, true);
|
||||
assert.equal(desktopCollapsed.wiring.headers.length, 2);
|
||||
assert.deepEqual(desktopCollapsed.wiring.legacyHeaderHits, []);
|
||||
assert.equal(desktopCollapsed.wiring.horizontalScroll.panelScrollWidth <= desktopCollapsed.wiring.horizontalScroll.panelClientWidth + 2, true);
|
||||
assert.equal(desktopCollapsed.resizeHandle.ok, false);
|
||||
const desktopDefault = report.checks.find((check) => check.id === "layout-desktop-default")?.observations;
|
||||
assert.equal(desktopDefault.removedSelectorsAbsent, true);
|
||||
assert.equal(desktopDefault.keyTargetsReachable, true);
|
||||
assert.equal(desktopDefault.wiring.longTableOk, true);
|
||||
assert.equal(desktopDefault.wiring.noHorizontalScroll, true);
|
||||
assert.equal(desktopDefault.wiring.headers.length, 2);
|
||||
assert.deepEqual(desktopDefault.wiring.legacyHeaderHits, []);
|
||||
assert.equal(desktopDefault.wiring.horizontalScroll.panelScrollWidth <= desktopDefault.wiring.horizontalScroll.panelClientWidth + 2, true);
|
||||
assert.equal(Object.hasOwn(desktopDefault.boxes.shell, "text"), false);
|
||||
assert.equal(Object.hasOwn(desktopDefault.semanticOverlapChecks[0].boxes["#m3-control-form"], "text"), false);
|
||||
assert.equal(desktopDefault.failures.length, 0);
|
||||
assert.equal(desktopDefault.semanticOverlapChecks.every((check) => !check.overlaps), true);
|
||||
assert.equal(desktopDefault.overflowChecks.every((check) => check.ok), true);
|
||||
|
||||
const mobileCollapsed = report.checks.find((check) => check.id === "layout-mobile-collapsed")?.observations;
|
||||
assert.equal(mobileCollapsed.explorerCollapsed, true);
|
||||
assert.equal(mobileCollapsed.defaultCollapsedMobile, true);
|
||||
assert.equal(mobileCollapsed.keyTargetsReachable, true);
|
||||
assert.equal(mobileCollapsed.wiring.longTableOk, true);
|
||||
assert.equal(mobileCollapsed.wiring.noHorizontalScroll, true);
|
||||
assert.equal(mobileCollapsed.resizeHandle.ok, false);
|
||||
|
||||
const desktopExpanded = report.checks.find((check) => check.id === "layout-desktop-expanded")?.observations;
|
||||
assert.equal(Object.hasOwn(desktopExpanded.boxes.shell, "text"), false);
|
||||
assert.equal(Object.hasOwn(desktopExpanded.semanticOverlapChecks[0].boxes["#m3-control-form"], "text"), false);
|
||||
assert.equal(desktopExpanded.failures.length, 0);
|
||||
assert.equal(desktopExpanded.semanticOverlapChecks.every((check) => !check.overlaps), true);
|
||||
assert.equal(desktopExpanded.overflowChecks.every((check) => check.ok), true);
|
||||
assert.equal(desktopExpanded.resizeHandle.ok, true);
|
||||
|
||||
const desktopResize = report.checks.find((check) => check.id === "layout-explorer-resize-desktop")?.observations;
|
||||
assert.equal(desktopResize.pass, true);
|
||||
assert.equal(desktopResize.before.handle.ariaLabel, "拖拽调整左侧资源树宽度");
|
||||
assert.equal(desktopResize.before.handle.role, "separator");
|
||||
assert.equal(Math.abs(desktopResize.afterDrag.explorerWidth - desktopResize.before.explorerWidth) >= 24, true);
|
||||
assert.equal(desktopResize.afterEnd.explorerWidth, desktopResize.afterEnd.bounds.max);
|
||||
assert.equal(desktopResize.afterHome.explorerWidth, desktopResize.afterHome.bounds.min);
|
||||
assert.equal(desktopResize.collapsed.explorerCollapsed, true);
|
||||
assert.equal(desktopResize.collapsed.handleTabIndex, -1);
|
||||
assert.equal(desktopResize.restored.storage.version, 1);
|
||||
assert.equal(desktopResize.restoredFromStorage.keyTargetsReachable, true);
|
||||
|
||||
const narrowResize = report.checks.find((check) => check.id === "layout-explorer-resize-narrow-desktop")?.observations;
|
||||
assert.equal(narrowResize.pass, true);
|
||||
assert.equal(narrowResize.afterEnd.explorerWidth <= narrowResize.afterEnd.bounds.max, true);
|
||||
assert.equal(narrowResize.restoredFromStorage.noHorizontalOverflow, true);
|
||||
|
||||
const mobileResize = report.checks.find((check) => check.id === "layout-explorer-resize-mobile")?.observations;
|
||||
assert.equal(mobileResize.pass, true);
|
||||
assert.equal(mobileResize.handleDisplay, "none");
|
||||
assert.equal(mobileResize.handleTabIndex, -1);
|
||||
const mobileDefault = report.checks.find((check) => check.id === "layout-mobile-default")?.observations;
|
||||
assert.equal(mobileDefault.removedSelectorsAbsent, true);
|
||||
assert.equal(mobileDefault.keyTargetsReachable, true);
|
||||
assert.equal(mobileDefault.wiring.longTableOk, true);
|
||||
assert.equal(mobileDefault.wiring.noHorizontalScroll, true);
|
||||
|
||||
const compact = compactLayoutSmokeCliOutput(report);
|
||||
assert.equal(compact.status, "pass");
|
||||
assert.equal(compact.artifacts.screenshotCount, 21);
|
||||
assert.equal(compact.artifacts.screenshotCount, 12);
|
||||
assert.equal(compact.failures.length, 0);
|
||||
assert.equal(compact.blockers.length, 0);
|
||||
assert.equal(JSON.stringify(compact).includes('"checks"'), false);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1
-272
@@ -79,9 +79,6 @@ const CODEX_STDIO_BLOCKER_MARKERS = Object.freeze([
|
||||
]);
|
||||
const UNTRUSTED_CODE_AGENT_PROVIDER_PATTERN = /\b(?:echo|mock|fixture|stub|sample)\b/iu;
|
||||
const LAYOUT_STORAGE_KEY = "hwlab.workbench.layout.v1";
|
||||
const EXPLORER_DEFAULT_WIDTH = 292;
|
||||
const EXPLORER_MIN_WIDTH = 220;
|
||||
const EXPLORER_MAX_WIDTH = 420;
|
||||
const RIGHT_SIDEBAR_DEFAULT_WIDTH = 620;
|
||||
const RIGHT_SIDEBAR_MIN_WIDTH = 560;
|
||||
const RIGHT_SIDEBAR_MAX_WIDTH = 760;
|
||||
@@ -91,14 +88,7 @@ let rpcSequence = 0;
|
||||
|
||||
const el = {
|
||||
shell: query("[data-app-shell]"),
|
||||
explorerToggle: byId("explorer-toggle"),
|
||||
explorerResize: byId("explorer-resize"),
|
||||
rightSidebarResize: byId("right-sidebar-resize"),
|
||||
explorerStatus: byId("explorer-status"),
|
||||
treeCount: byId("tree-count"),
|
||||
resourceTree: byId("resource-tree"),
|
||||
capabilityCount: byId("capability-count"),
|
||||
explorerCapabilities: byId("explorer-capabilities"),
|
||||
routePath: byId("route-path"),
|
||||
liveStatus: byId("live-status"),
|
||||
liveDetail: byId("live-detail"),
|
||||
@@ -159,18 +149,13 @@ const state = {
|
||||
pending: false
|
||||
},
|
||||
layout: {
|
||||
explorerWidth: EXPLORER_DEFAULT_WIDTH,
|
||||
rightSidebarWidth: RIGHT_SIDEBAR_DEFAULT_WIDTH,
|
||||
drag: null,
|
||||
rightDrag: null
|
||||
}
|
||||
};
|
||||
|
||||
initRoutes();
|
||||
initLayoutSizing();
|
||||
initExplorerToggle();
|
||||
syncMobileExplorer();
|
||||
initExplorerResize();
|
||||
initRightSidebarResize();
|
||||
initSideTabs();
|
||||
initHardwareTabs();
|
||||
@@ -219,9 +204,7 @@ function initRoutes() {
|
||||
|
||||
function initLayoutSizing() {
|
||||
const persisted = readPersistedLayout();
|
||||
const explorerWidth = clampExplorerWidth(persisted?.explorerWidth ?? EXPLORER_DEFAULT_WIDTH);
|
||||
const rightSidebarWidth = clampRightSidebarWidth(persisted?.rightSidebarWidth ?? RIGHT_SIDEBAR_DEFAULT_WIDTH);
|
||||
setExplorerWidth(explorerWidth, { persist: false });
|
||||
setRightSidebarWidth(rightSidebarWidth, { persist: false });
|
||||
}
|
||||
|
||||
@@ -232,7 +215,6 @@ function readPersistedLayout() {
|
||||
const payload = JSON.parse(raw);
|
||||
if (payload?.version !== 1) return null;
|
||||
return {
|
||||
explorerWidth: Number(payload.explorerWidth),
|
||||
rightSidebarWidth: Number(payload.rightSidebarWidth)
|
||||
};
|
||||
} catch {
|
||||
@@ -246,7 +228,6 @@ function persistLayout() {
|
||||
LAYOUT_STORAGE_KEY,
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
explorerWidth: state.layout.explorerWidth,
|
||||
rightSidebarWidth: state.layout.rightSidebarWidth
|
||||
})
|
||||
);
|
||||
@@ -255,16 +236,6 @@ function persistLayout() {
|
||||
}
|
||||
}
|
||||
|
||||
function setExplorerWidth(width, options = {}) {
|
||||
const clamped = clampExplorerWidth(width);
|
||||
state.layout.explorerWidth = clamped;
|
||||
el.shell.style.setProperty("--explorer-width", `${clamped}px`);
|
||||
syncExplorerResizeA11y();
|
||||
syncRightSidebarResizeA11y();
|
||||
if (options.persist !== false) persistLayout();
|
||||
return clamped;
|
||||
}
|
||||
|
||||
function setRightSidebarWidth(width, options = {}) {
|
||||
const clamped = clampRightSidebarWidth(width);
|
||||
state.layout.rightSidebarWidth = clamped;
|
||||
@@ -274,13 +245,6 @@ function setRightSidebarWidth(width, options = {}) {
|
||||
return clamped;
|
||||
}
|
||||
|
||||
function clampExplorerWidth(value) {
|
||||
const numeric = Number(value);
|
||||
const width = Number.isFinite(numeric) ? numeric : EXPLORER_DEFAULT_WIDTH;
|
||||
const bounds = explorerResizeBounds();
|
||||
return Math.min(Math.max(Math.round(width), bounds.min), bounds.max);
|
||||
}
|
||||
|
||||
function clampRightSidebarWidth(value) {
|
||||
const numeric = Number(value);
|
||||
const width = Number.isFinite(numeric) ? numeric : RIGHT_SIDEBAR_DEFAULT_WIDTH;
|
||||
@@ -288,24 +252,6 @@ function clampRightSidebarWidth(value) {
|
||||
return Math.min(Math.max(Math.round(width), bounds.min), bounds.max);
|
||||
}
|
||||
|
||||
function explorerResizeBounds() {
|
||||
const styles = getComputedStyle(el.shell);
|
||||
const cssMin = readCssPixel(styles.getPropertyValue("--explorer-min-width"), EXPLORER_MIN_WIDTH);
|
||||
const cssMax = readCssPixel(styles.getPropertyValue("--explorer-max-width"), EXPLORER_MAX_WIDTH);
|
||||
if (window.matchMedia("(max-width: 860px)").matches) {
|
||||
return { min: EXPLORER_MIN_WIDTH, max: EXPLORER_MAX_WIDTH };
|
||||
}
|
||||
if (window.matchMedia("(max-width: 1240px)").matches) {
|
||||
const railWidth = readCssPixel(styles.getPropertyValue("--rail-width"), 86);
|
||||
const dynamicMax = window.innerWidth - railWidth - 420 - 4;
|
||||
return boundedResizeRange(cssMin, cssMax, dynamicMax);
|
||||
}
|
||||
const railWidth = readCssPixel(styles.getPropertyValue("--rail-width"), 92);
|
||||
const rightWidth = Math.min(Math.max(window.innerWidth * 0.45, 560), 740);
|
||||
const dynamicMax = window.innerWidth - railWidth - 360 - rightWidth - 4;
|
||||
return boundedResizeRange(cssMin, cssMax, dynamicMax);
|
||||
}
|
||||
|
||||
function boundedResizeRange(min, max, dynamicMax) {
|
||||
const safeMin = Math.max(0, Math.round(min));
|
||||
const safeMax = Math.max(safeMin, Math.round(Math.min(max, Number.isFinite(dynamicMax) ? dynamicMax : max)));
|
||||
@@ -320,9 +266,7 @@ function rightSidebarResizeBounds() {
|
||||
return { min: 0, max: Math.max(0, window.innerWidth) };
|
||||
}
|
||||
const railWidth = readCssPixel(styles.getPropertyValue("--rail-width"), 92);
|
||||
const explorerWidth = el.shell.classList.contains("explorer-collapsed") ? 0 : state.layout.explorerWidth;
|
||||
const centerMinWidth = el.shell.classList.contains("explorer-collapsed") ? 420 : 360;
|
||||
const dynamicMax = window.innerWidth - railWidth - explorerWidth - centerMinWidth - 4;
|
||||
const dynamicMax = window.innerWidth - railWidth - 420 - 4;
|
||||
return boundedResizeRange(cssMin, cssMax, dynamicMax);
|
||||
}
|
||||
|
||||
@@ -331,14 +275,6 @@ function readCssPixel(value, fallback) {
|
||||
return Number.isFinite(parsed) ? parsed : fallback;
|
||||
}
|
||||
|
||||
function syncExplorerResizeA11y() {
|
||||
const bounds = explorerResizeBounds();
|
||||
el.explorerResize.setAttribute("aria-valuemin", String(bounds.min));
|
||||
el.explorerResize.setAttribute("aria-valuemax", String(bounds.max));
|
||||
el.explorerResize.setAttribute("aria-valuenow", String(state.layout.explorerWidth));
|
||||
el.explorerResize.setAttribute("aria-valuetext", `左侧资源树宽度 ${state.layout.explorerWidth} 像素`);
|
||||
}
|
||||
|
||||
function syncRightSidebarResizeA11y() {
|
||||
const bounds = rightSidebarResizeBounds();
|
||||
const disabled = !canResizeRightSidebar();
|
||||
@@ -374,57 +310,6 @@ function showView(route) {
|
||||
}
|
||||
}
|
||||
|
||||
function initExplorerToggle() {
|
||||
el.explorerToggle.addEventListener("click", () => {
|
||||
const collapsed = !el.shell.classList.contains("explorer-collapsed");
|
||||
setExplorerCollapsed(collapsed);
|
||||
});
|
||||
}
|
||||
|
||||
function initExplorerResize() {
|
||||
window.addEventListener("resize", () => {
|
||||
if (canResizeExplorer()) setExplorerWidth(state.layout.explorerWidth, { persist: false });
|
||||
syncExplorerResizeA11y();
|
||||
});
|
||||
el.explorerResize.addEventListener("pointerdown", (event) => {
|
||||
if (!canResizeExplorer()) return;
|
||||
event.preventDefault();
|
||||
el.explorerResize.setPointerCapture?.(event.pointerId);
|
||||
state.layout.drag = {
|
||||
pointerId: event.pointerId,
|
||||
startX: event.clientX,
|
||||
startWidth: state.layout.explorerWidth
|
||||
};
|
||||
el.shell.classList.add("is-resizing");
|
||||
});
|
||||
el.explorerResize.addEventListener("pointermove", (event) => {
|
||||
const drag = state.layout.drag;
|
||||
if (!drag || drag.pointerId !== event.pointerId) return;
|
||||
setExplorerWidth(drag.startWidth + event.clientX - drag.startX, { persist: false });
|
||||
});
|
||||
el.explorerResize.addEventListener("pointerup", finishExplorerResize);
|
||||
el.explorerResize.addEventListener("pointercancel", finishExplorerResize);
|
||||
el.explorerResize.addEventListener("keydown", (event) => {
|
||||
if (!canResizeExplorer()) return;
|
||||
const keySteps = {
|
||||
ArrowLeft: -16,
|
||||
ArrowRight: 16,
|
||||
PageDown: -48,
|
||||
PageUp: 48
|
||||
};
|
||||
if (Object.hasOwn(keySteps, event.key)) {
|
||||
event.preventDefault();
|
||||
setExplorerWidth(state.layout.explorerWidth + keySteps[event.key]);
|
||||
} else if (event.key === "Home") {
|
||||
event.preventDefault();
|
||||
setExplorerWidth(explorerResizeBounds().min);
|
||||
} else if (event.key === "End") {
|
||||
event.preventDefault();
|
||||
setExplorerWidth(explorerResizeBounds().max);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initRightSidebarResize() {
|
||||
window.addEventListener("resize", () => {
|
||||
if (canResizeRightSidebar()) setRightSidebarWidth(state.layout.rightSidebarWidth, { persist: false });
|
||||
@@ -469,14 +354,6 @@ function initRightSidebarResize() {
|
||||
});
|
||||
}
|
||||
|
||||
function finishExplorerResize(event) {
|
||||
const drag = state.layout.drag;
|
||||
if (!drag || drag.pointerId !== event.pointerId) return;
|
||||
state.layout.drag = null;
|
||||
el.shell.classList.remove("is-resizing");
|
||||
persistLayout();
|
||||
}
|
||||
|
||||
function finishRightSidebarResize(event) {
|
||||
const drag = state.layout.rightDrag;
|
||||
if (!drag || drag.pointerId !== event.pointerId) return;
|
||||
@@ -485,45 +362,10 @@ function finishRightSidebarResize(event) {
|
||||
persistLayout();
|
||||
}
|
||||
|
||||
function canResizeExplorer() {
|
||||
return !window.matchMedia("(max-width: 860px)").matches && !el.shell.classList.contains("explorer-collapsed");
|
||||
}
|
||||
|
||||
function canResizeRightSidebar() {
|
||||
return !window.matchMedia("(max-width: 1240px)").matches;
|
||||
}
|
||||
|
||||
function setExplorerCollapsed(collapsed) {
|
||||
if (!collapsed) {
|
||||
setExplorerWidth(state.layout.explorerWidth, { persist: false });
|
||||
}
|
||||
el.shell.classList.toggle("explorer-collapsed", collapsed);
|
||||
el.explorerToggle.setAttribute("aria-pressed", collapsed ? "true" : "false");
|
||||
el.explorerToggle.setAttribute("aria-expanded", collapsed ? "false" : "true");
|
||||
el.explorerToggle.setAttribute("aria-label", collapsed ? "展开左侧资源树" : "收起左侧资源树");
|
||||
el.explorerToggle.title = collapsed ? "展开左侧资源树,恢复导航和硬件资源" : "收起左侧资源树,给工作区更多宽度";
|
||||
el.explorerToggle.textContent = collapsed ? "展开资源树" : "收起资源树";
|
||||
const resizeDisabled = collapsed || window.matchMedia("(max-width: 860px)").matches;
|
||||
el.explorerResize.tabIndex = resizeDisabled ? -1 : 0;
|
||||
el.explorerResize.setAttribute("aria-disabled", resizeDisabled ? "true" : "false");
|
||||
el.explorerResize.setAttribute("aria-hidden", resizeDisabled ? "true" : "false");
|
||||
setRightSidebarWidth(state.layout.rightSidebarWidth, { persist: false });
|
||||
}
|
||||
|
||||
function syncMobileExplorer() {
|
||||
const media = window.matchMedia("(max-width: 860px)");
|
||||
const apply = () => {
|
||||
if (media.matches) {
|
||||
setExplorerCollapsed(true);
|
||||
} else {
|
||||
setExplorerCollapsed(false);
|
||||
setExplorerWidth(state.layout.explorerWidth, { persist: false });
|
||||
}
|
||||
};
|
||||
apply();
|
||||
media.addEventListener("change", apply);
|
||||
}
|
||||
|
||||
async function loadHelpSurface() {
|
||||
el.helpStatus.textContent = "加载中";
|
||||
el.helpStatus.className = "state-tag tone-dry-run";
|
||||
@@ -558,22 +400,14 @@ function initSideTabs() {
|
||||
for (const jump of document.querySelectorAll("[data-side-tab-jump]")) {
|
||||
jump.addEventListener("click", () => {
|
||||
selectSideTab(jump.dataset.sideTabJump);
|
||||
collapseExplorerAfterMobileAction();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initQuickActions() {
|
||||
for (const button of document.querySelectorAll("[data-focus-command]")) {
|
||||
button.addEventListener("click", () => {
|
||||
el.commandInput.focus();
|
||||
collapseExplorerAfterMobileAction();
|
||||
});
|
||||
}
|
||||
for (const button of document.querySelectorAll("[data-agent-quick-prompt]")) {
|
||||
button.addEventListener("click", () => {
|
||||
fillAgentQuickPrompt(button);
|
||||
collapseExplorerAfterMobileAction();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -586,12 +420,6 @@ function fillAgentQuickPrompt(button) {
|
||||
el.commandInput.setSelectionRange(prompt.length, prompt.length);
|
||||
}
|
||||
|
||||
function collapseExplorerAfterMobileAction() {
|
||||
if (window.matchMedia("(max-width: 860px)").matches) {
|
||||
setExplorerCollapsed(true);
|
||||
}
|
||||
}
|
||||
|
||||
function selectSideTab(tabId) {
|
||||
for (const tab of document.querySelectorAll("[data-side-tab]")) {
|
||||
const active = tab.dataset.sideTab === tabId;
|
||||
@@ -788,12 +616,8 @@ function initGateControls() {
|
||||
}
|
||||
|
||||
function renderStaticWorkbench() {
|
||||
el.explorerStatus.textContent = "只读工作区";
|
||||
el.explorerStatus.className = "status-dot tone-source";
|
||||
el.routePath.textContent = "当前浏览器会话只保存任务草稿,硬件动作需通过受控后端流程。";
|
||||
|
||||
renderResourceTree();
|
||||
renderCapabilityList();
|
||||
renderConversation();
|
||||
renderTaskList();
|
||||
renderHardwareStatus(null);
|
||||
@@ -1125,86 +949,6 @@ function workbenchApiSurfaceStatus(live, coreProbes = [live.healthLive, live.res
|
||||
};
|
||||
}
|
||||
|
||||
function renderResourceTree() {
|
||||
const items = [
|
||||
{
|
||||
label: gateSummary.topology.projectId,
|
||||
meta: "当前项目",
|
||||
tone: "source",
|
||||
children: [
|
||||
...gateSummary.topology.gateways.map((gateway) => ({
|
||||
label: gateway.gatewaySessionId,
|
||||
meta: `${gateway.serviceId} / ${statusLabel(gateway.status)}`,
|
||||
tone: gateway.status
|
||||
})),
|
||||
...gateSummary.topology.boxResources.map((resource) => ({
|
||||
label: resource.resourceId,
|
||||
meta: `${resourceTypeLabel(resource.resourceType)} / ${statusLabel(resource.state)}`,
|
||||
tone: resource.state
|
||||
})),
|
||||
{
|
||||
label: gateSummary.topology.patchPanel.patchPanelStatusId,
|
||||
meta: `${gateSummary.topology.patchPanel.serviceId} / ${statusLabel(gateSummary.topology.patchPanel.state)}`,
|
||||
tone: gateSummary.topology.patchPanel.state
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: gateSummary.agent.agentSessionId,
|
||||
meta: `${gateSummary.agent.agentServiceId} / Agent 会话`,
|
||||
tone: gateSummary.agent.agentStatus,
|
||||
children: [
|
||||
{
|
||||
label: gateSummary.agent.workerSessionId,
|
||||
meta: `${gateSummary.agent.workerServiceId} / ${statusLabel(gateSummary.agent.workerStatus)}`,
|
||||
tone: gateSummary.agent.workerStatus
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
el.treeCount.textContent = String(items.reduce((count, item) => count + 1 + (item.children?.length ?? 0), 0));
|
||||
replaceChildren(el.resourceTree, ...items.map(treeGroup));
|
||||
}
|
||||
|
||||
function treeGroup(item) {
|
||||
const details = document.createElement("details");
|
||||
details.open = true;
|
||||
const summary = document.createElement("summary");
|
||||
summary.append(statusLight(item.tone), textSpan(item.label, "tree-label"), textSpan(item.meta, "tree-meta"));
|
||||
details.append(summary);
|
||||
for (const child of item.children ?? []) {
|
||||
const row = document.createElement("button");
|
||||
row.type = "button";
|
||||
row.className = "tree-row";
|
||||
row.append(statusLight(child.tone), textSpan(child.label, "tree-label"), textSpan(child.meta, "tree-meta"));
|
||||
details.append(row);
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
function renderCapabilityList() {
|
||||
const capabilities = [
|
||||
{
|
||||
title: "任务草稿",
|
||||
detail: "在底部输入区记录要交给 Agent 的任务,不会直接发送硬件请求。",
|
||||
tone: "source"
|
||||
},
|
||||
{
|
||||
title: "资源查看",
|
||||
detail: "查看 Gateway-SIMU、BOX-SIMU 与 hwlab-patch-panel 的只读资源状态。",
|
||||
tone: "source"
|
||||
},
|
||||
{
|
||||
title: "接线核对",
|
||||
detail: "在右侧两列接线表核对互连设备、DO/DI 对应关系与证据记录。",
|
||||
tone: "source"
|
||||
}
|
||||
];
|
||||
el.capabilityCount.textContent = String(capabilities.length);
|
||||
replaceChildren(el.explorerCapabilities, ...capabilities.map(infoCard));
|
||||
}
|
||||
|
||||
function renderConversation() {
|
||||
const introMessages = [
|
||||
{
|
||||
@@ -2785,12 +2529,6 @@ function infoCard(item) {
|
||||
return article;
|
||||
}
|
||||
|
||||
function statusLight(tone) {
|
||||
const span = document.createElement("span");
|
||||
span.className = `tree-light tone-bg-${toneClass(tone)}`;
|
||||
return span;
|
||||
}
|
||||
|
||||
function badge(text, tone = text) {
|
||||
const span = document.createElement("span");
|
||||
span.className = `badge tone-${toneClass(tone)}`;
|
||||
@@ -3252,15 +2990,6 @@ function roleLabel(role) {
|
||||
);
|
||||
}
|
||||
|
||||
function resourceTypeLabel(type) {
|
||||
return (
|
||||
{
|
||||
board: "板卡",
|
||||
relay: "继电器"
|
||||
}[String(type ?? "").toLowerCase()] ?? statusLabel(type)
|
||||
);
|
||||
}
|
||||
|
||||
function recordKindLabel(kind) {
|
||||
return (
|
||||
{
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
本页面说明当前 Cloud Workbench 的前端区域与状态含义。默认 `/` 仍进入工作台;本说明只通过工作台内部的 `#help` 入口打开,不作为默认页。
|
||||
|
||||
## 左侧资源与功能导航
|
||||
## 活动栏与默认工作区
|
||||
|
||||
- 活动栏使用完整中文标签:`工作台` 回到默认工作台,`内部复核` 打开只读复核表,`使用说明` 打开本说明,`资源树` 展开或收起资源树。
|
||||
- 活动栏使用完整中文标签:`工作台` 回到默认工作台,`内部复核` 打开只读复核表,`使用说明` 打开本说明。
|
||||
- 左侧不再放独立的可信记录跳转;可信记录保留在右侧子面板。
|
||||
- 资源树只展示当前项目、Gateway-SIMU、BOX-SIMU、`hwlab-patch-panel`、Agent manager / worker 等用户可理解资源,不把 M0-M5 Gate 状态放在默认首屏。
|
||||
- 常用能力区提供任务草稿、资源查看与接线核对入口。`LOCAL` 只表示浏览器本地草稿,不等同于 DEV-LIVE。
|
||||
- 默认首屏不再放资源树、能力卡片或左侧抽屉,空间直接分配给 Code Agent 对话、执行证据、硬件状态、接线和控制。
|
||||
- `LOCAL` 只表示浏览器本地草稿,不等同于 DEV-LIVE。
|
||||
|
||||
## 中间 Agent 对话与工作清单
|
||||
|
||||
|
||||
@@ -14,49 +14,6 @@
|
||||
<button class="rail-button" type="button" data-route="gate" title="内部复核" aria-label="内部复核">内部复核</button>
|
||||
<button class="rail-button" type="button" data-route="help" title="使用说明" aria-label="使用说明">使用说明</button>
|
||||
<span class="rail-spacer" aria-hidden="true"></span>
|
||||
<button class="rail-button" id="explorer-toggle" type="button" title="收起左侧资源树,给工作区更多宽度" aria-label="收起左侧资源树" aria-controls="resource-explorer" aria-expanded="true" aria-pressed="false">收起资源树</button>
|
||||
</aside>
|
||||
|
||||
<aside class="explorer" id="resource-explorer" aria-label="资源浏览与硬件资源树">
|
||||
<header class="explorer-head">
|
||||
<div>
|
||||
<p class="eyebrow">HWLAB Cloud</p>
|
||||
<h1>云工作台</h1>
|
||||
</div>
|
||||
<span class="status-dot tone-source" id="explorer-status">只读工作区</span>
|
||||
</header>
|
||||
<section class="quick-actions" aria-label="常用工作入口">
|
||||
<button type="button" data-focus-command>编写任务</button>
|
||||
<button type="button" data-side-tab-jump="wiring">查看接线</button>
|
||||
<button type="button" data-side-tab-jump="control">查看状态</button>
|
||||
</section>
|
||||
<section class="tree-panel">
|
||||
<div class="panel-title-row">
|
||||
<h2>硬件资源</h2>
|
||||
<span id="tree-count">0</span>
|
||||
</div>
|
||||
<nav id="resource-tree" class="resource-tree" aria-label="HWLAB 硬件资源"></nav>
|
||||
</section>
|
||||
<section class="tree-panel blockers-panel">
|
||||
<div class="panel-title-row">
|
||||
<h2>常用能力</h2>
|
||||
<span id="capability-count">0</span>
|
||||
</div>
|
||||
<div id="explorer-capabilities" class="compact-list"></div>
|
||||
</section>
|
||||
<div
|
||||
class="resize-handle explorer-resize"
|
||||
id="explorer-resize"
|
||||
role="separator"
|
||||
tabindex="0"
|
||||
aria-label="拖拽调整左侧资源树宽度"
|
||||
aria-controls="resource-explorer"
|
||||
aria-orientation="vertical"
|
||||
aria-valuemin="220"
|
||||
aria-valuemax="420"
|
||||
aria-valuenow="292"
|
||||
title="拖拽调整左侧资源树宽度;用左右方向键微调,Home/End 到最小或最大。"
|
||||
></div>
|
||||
</aside>
|
||||
|
||||
<section class="center-workspace">
|
||||
|
||||
@@ -245,17 +245,9 @@ assert.equal(
|
||||
"#288 single-table gate is future coverage; current /gate route remains covered by layout smoke"
|
||||
);
|
||||
for (const layoutCheckId of [
|
||||
"layout-desktop-expanded",
|
||||
"layout-desktop-collapsed",
|
||||
"layout-desktop-restored",
|
||||
"layout-narrow-desktop-expanded",
|
||||
"layout-narrow-desktop-collapsed",
|
||||
"layout-narrow-desktop-restored",
|
||||
"layout-mobile-collapsed",
|
||||
"layout-mobile-drawer",
|
||||
"layout-explorer-resize-desktop",
|
||||
"layout-explorer-resize-narrow-desktop",
|
||||
"layout-explorer-resize-mobile",
|
||||
"layout-desktop-default",
|
||||
"layout-narrow-desktop-default",
|
||||
"layout-mobile-default",
|
||||
"layout-right-sidebar-resize-desktop",
|
||||
"layout-right-sidebar-resize-narrow-desktop",
|
||||
"layout-right-sidebar-resize-mobile",
|
||||
@@ -387,10 +379,6 @@ assert.match(html, /href="\/styles\.css"/);
|
||||
assert.match(html, /src="\/app\.mjs"/);
|
||||
for (const workbenchElement of [
|
||||
"activity-rail",
|
||||
"explorer",
|
||||
"explorer-resize",
|
||||
"resource-explorer",
|
||||
"resource-tree",
|
||||
"conversation-list",
|
||||
"agent-chat-status",
|
||||
"code-agent-summary",
|
||||
@@ -410,29 +398,41 @@ for (const workbenchElement of [
|
||||
]) {
|
||||
assert.match(html, new RegExp(workbenchElement));
|
||||
}
|
||||
assert.match(html, /id="explorer-toggle"/);
|
||||
assert.match(html, /aria-controls="resource-explorer"/);
|
||||
assert.match(html, /aria-expanded="true"/);
|
||||
assert.match(html, /aria-pressed="false"/);
|
||||
assert.match(html, /aria-label="收起左侧资源树"/);
|
||||
assert.match(html, />收起资源树<\/button>/);
|
||||
assert.match(html, /id="explorer-resize"/);
|
||||
assert.match(html, /role="separator"/);
|
||||
assert.match(html, /aria-label="拖拽调整左侧资源树宽度"/);
|
||||
assert.match(html, /aria-valuemin="220"/);
|
||||
assert.match(html, /aria-valuemax="420"/);
|
||||
assert.match(html, /aria-valuenow="292"/);
|
||||
for (const removedSelectorTerm of [
|
||||
"id=\"resource-explorer\"",
|
||||
"id=\"explorer-toggle\"",
|
||||
"id=\"explorer-resize\"",
|
||||
"id=\"resource-tree\"",
|
||||
"explorer-capabilities",
|
||||
"capability-count",
|
||||
"tree-count",
|
||||
"quick-actions",
|
||||
"data-focus-command"
|
||||
]) {
|
||||
assert.doesNotMatch(html, new RegExp(escapeRegExp(removedSelectorTerm)), `removed resource explorer selector returned: ${removedSelectorTerm}`);
|
||||
}
|
||||
for (const removedUiText of [
|
||||
"资源浏览与硬件资源树",
|
||||
"只读工作区",
|
||||
"常用能力",
|
||||
"收起资源树",
|
||||
"展开资源树",
|
||||
"拖拽调整左侧资源树宽度",
|
||||
"资源查看",
|
||||
"查看接线",
|
||||
"查看状态",
|
||||
"编写任务"
|
||||
]) {
|
||||
assert.doesNotMatch(`${html}\n${app}\n${styles}`, new RegExp(escapeRegExp(removedUiText)), `removed resource explorer copy returned: ${removedUiText}`);
|
||||
}
|
||||
assert.match(html, /id="hardware-sidebar"/);
|
||||
assert.match(html, /id="right-sidebar-resize"/);
|
||||
assert.match(html, /aria-label="拖拽调整右侧硬件状态栏宽度"/);
|
||||
assert.match(html, /aria-valuemin="560"/);
|
||||
assert.match(html, /aria-valuemax="760"/);
|
||||
assert.match(html, /aria-valuenow="620"/);
|
||||
assert.match(app, /setAttribute\("aria-label", collapsed \? "展开左侧资源树" : "收起左侧资源树"\)/);
|
||||
assert.match(app, /textContent = collapsed \? "展开资源树" : "收起资源树"/);
|
||||
assert.doesNotMatch(app, /setExplorerCollapsed|initExplorerResize|clampExplorerWidth|syncMobileExplorer|collapseExplorerAfterMobileAction/);
|
||||
assert.match(app, /LAYOUT_STORAGE_KEY\s*=\s*"hwlab\.workbench\.layout\.v1"/);
|
||||
assert.match(app, /function initExplorerResize/);
|
||||
assert.match(app, /function clampExplorerWidth/);
|
||||
assert.match(app, /function initRightSidebarResize/);
|
||||
assert.match(app, /function clampRightSidebarWidth/);
|
||||
assert.match(app, /function rightSidebarResizeBounds/);
|
||||
@@ -443,20 +443,14 @@ assert.match(app, /ArrowLeft/);
|
||||
assert.match(app, /ArrowRight/);
|
||||
assert.match(app, /event\.key === "Home"/);
|
||||
assert.match(app, /event\.key === "End"/);
|
||||
assert.match(styles, /--explorer-width:\s*292px/);
|
||||
assert.match(styles, /--explorer-min-width:\s*220px/);
|
||||
assert.match(styles, /--explorer-max-width:\s*420px/);
|
||||
assert.doesNotMatch(styles, /--explorer-width|--explorer-min-width|--explorer-max-width|\.explorer|\.explorer-resize|\.quick-actions|\.resource-tree|\.tree-panel|\.tree-row|\.tree-label/);
|
||||
assert.match(styles, /--right-sidebar-width:\s*620px/);
|
||||
assert.match(styles, /--right-sidebar-min-width:\s*560px/);
|
||||
assert.match(styles, /--right-sidebar-max-width:\s*760px/);
|
||||
assert.match(styles, /--right-width:\s*clamp\(var\(--right-sidebar-min-width\),\s*var\(--right-sidebar-width\),\s*var\(--right-sidebar-max-width\)\)/);
|
||||
assert.match(styles, /--right-width-expanded:\s*clamp\(var\(--right-sidebar-min-width\),\s*var\(--right-sidebar-width\),\s*var\(--right-sidebar-max-width\)\)/);
|
||||
assert.match(styles, /\.explorer-resize\s*{[^}]*cursor:\s*col-resize;/s);
|
||||
assert.match(styles, /\.right-sidebar-resize\s*{[^}]*cursor:\s*col-resize;/s);
|
||||
assert.match(styles, /\.explorer-collapsed \.explorer-resize\s*{[^}]*display:\s*none;/s);
|
||||
assert.match(styles, /@media \(max-width: 860px\)[\s\S]*?\.explorer-resize\s*{[\s\S]*?display:\s*none;/);
|
||||
assert.match(styles, /@media \(max-width: 1240px\)[\s\S]*?\.right-sidebar-resize\s*{[\s\S]*?display:\s*none;/);
|
||||
assert.match(styles, /\.explorer-collapsed\s*\{[^}]*--explorer-width:\s*0px;[^}]*grid-template-columns:\s*var\(--rail-width\)\s+0\s+minmax\(420px,\s*1fr\)\s+var\(--right-width\);/s);
|
||||
for (const viewId of ["workspace", "gate"]) {
|
||||
assert.match(html, new RegExp(`data-view="${viewId}"`));
|
||||
}
|
||||
@@ -481,7 +475,7 @@ assert.match(styles, /body\s*>\s*\[data-app-shell\]\s*{[^}]*height:\s*100%;[^}]*
|
||||
assert.match(styles, /\.workbench-shell\s*{[^}]*height:\s*100dvh;[^}]*max-height:\s*100dvh;[^}]*overflow:\s*hidden;[^}]*overscroll-behavior:\s*contain;/s);
|
||||
assert.match(helpMarkdown, /^# 云工作台内部使用说明/m);
|
||||
for (const helpTerm of [
|
||||
"左侧资源与功能导航",
|
||||
"活动栏与默认工作区",
|
||||
"Agent 对话与工作清单",
|
||||
"完整中文标签",
|
||||
"内部复核",
|
||||
@@ -512,7 +506,7 @@ const defaultFirstViewportText = visibleTextFromHtml(defaultFirstViewportHtml(ht
|
||||
for (const backendTerm of ["Gate", "诊断", "验收", "BLOCKED", "SOURCE", "DRY-RUN", "DEV-LIVE", "M0-M5", "M3 基本", "trace/evidence", "执行轨迹", "OPENAI_API_KEY", "Secret", "secretRef", "hwlab-code-agent-provider", "m3-route-required", "waiting-for-dev-live"]) {
|
||||
assert.equal(textHasForbiddenTerm(defaultFirstViewportText, backendTerm), false, `default workbench must not expose backend term ${backendTerm}`);
|
||||
}
|
||||
for (const fullNavLabel of ["工作台", "内部复核", "使用说明", "收起资源树"]) {
|
||||
for (const fullNavLabel of ["工作台", "内部复核", "使用说明"]) {
|
||||
assert.match(html, new RegExp(`>${escapeRegExp(fullNavLabel)}<`), `missing full nav label ${fullNavLabel}`);
|
||||
}
|
||||
for (const oneCharNavLabel of [">台<", ">证<", ">诊<", ">帮<"]) {
|
||||
@@ -625,10 +619,6 @@ assert.doesNotMatch(app, /API 错误 \/ 只读模式/);
|
||||
assert.doesNotMatch(app, /可信记录受阻 \/ 只读模式/);
|
||||
assert.doesNotMatch(app, /API 降级 \/ 只读模式/);
|
||||
assert.doesNotMatch(functionBody(app, "workbenchApiSurfaceStatus"), /runtime_durable_adapter_query_blocked|DB live readiness|Gate|诊断|验收|M0-M5|执行轨迹/u);
|
||||
assert.match(html, /quick-actions/);
|
||||
assert.match(html, /编写任务/);
|
||||
assert.match(html, /查看接线/);
|
||||
assert.match(html, /查看状态/);
|
||||
assert.match(html, /placeholder="输入要发送给 Code Agent 的消息;不会直接触发硬件变更。"/);
|
||||
assert.match(html, />发送<\/button>/);
|
||||
assert.doesNotMatch(html, />添加草稿<\/button>/);
|
||||
@@ -828,15 +818,8 @@ assert.match(styles, /html,\s*\nbody\s*{[^}]*height:\s*100%;[^}]*overflow:\s*hid
|
||||
assert.match(styles, /body\s*>\s*\[data-app-shell\]\s*{[^}]*min-height:\s*0;/s);
|
||||
assert.match(styles, /\.workbench-shell\s*{[^}]*height:\s*100(?:d)?vh;[^}]*overflow:\s*hidden;/s);
|
||||
assert.match(styles, /\.view\s*{[^}]*min-height:\s*0;[^}]*overflow:\s*auto;/s);
|
||||
assert.match(styles, /\.(?:resource-tree|compact-list|conversation-list|task-list|hardware-list)[^{]*{[^}]*min-height:\s*0;[^}]*overflow:\s*auto;/s);
|
||||
assert.match(app, /function syncMobileExplorer/);
|
||||
assert.match(app, /window\.matchMedia\("\(max-width: 860px\)"\)/);
|
||||
assert.match(app, /setExplorerCollapsed\(true\)/);
|
||||
assert.match(app, /function collapseExplorerAfterMobileAction/);
|
||||
assert.match(functionBody(app, "setExplorerCollapsed"), /aria-expanded/);
|
||||
assert.match(styles, /\.explorer-collapsed \.explorer\s*{[^}]*pointer-events:\s*none;/s);
|
||||
assert.match(styles, /\.(?:compact-list|conversation-list|task-list|hardware-list)[^{]*{[^}]*min-height:\s*0;[^}]*overflow:\s*auto;/s);
|
||||
assert.match(styles, /@media \(max-width: 860px\)[\s\S]*?\.activity-rail\s*{[\s\S]*?grid-row:\s*1 \/ 3;/);
|
||||
assert.match(styles, /@media \(max-width: 860px\)[\s\S]*?\.explorer\s*{[\s\S]*?grid-row:\s*1 \/ 3;[\s\S]*?z-index:\s*4;/);
|
||||
assert.match(styles, /@media \(max-width: 860px\)[\s\S]*?\.center-workspace\s*{[\s\S]*?grid-row:\s*1;/);
|
||||
assert.match(styles, /@media \(max-width: 860px\)[\s\S]*?\.right-sidebar\s*{[\s\S]*?grid-row:\s*2;/);
|
||||
assert.match(styles, /@media \(max-width: 520px\)[\s\S]*?\.command-bar\s*{[\s\S]*?grid-template-columns:\s*minmax\(0, 1fr\) auto auto;/);
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
--bad: #e76e5e;
|
||||
--info: #7ea6c9;
|
||||
--rail-width: 92px;
|
||||
--explorer-width: 292px;
|
||||
--explorer-min-width: 220px;
|
||||
--explorer-max-width: 420px;
|
||||
--right-sidebar-width: 620px;
|
||||
--right-sidebar-min-width: 560px;
|
||||
--right-sidebar-max-width: 760px;
|
||||
@@ -83,7 +80,7 @@ ul {
|
||||
max-height: 100dvh;
|
||||
min-height: 0;
|
||||
display: grid;
|
||||
grid-template-columns: var(--rail-width) var(--explorer-width) minmax(360px, 1fr) var(--right-width-expanded);
|
||||
grid-template-columns: var(--rail-width) minmax(420px, 1fr) var(--right-width-expanded);
|
||||
grid-template-rows: 1fr;
|
||||
overflow: hidden;
|
||||
overscroll-behavior: contain;
|
||||
@@ -91,7 +88,6 @@ ul {
|
||||
}
|
||||
|
||||
.activity-rail,
|
||||
.explorer,
|
||||
.right-sidebar,
|
||||
.center-workspace {
|
||||
min-width: 0;
|
||||
@@ -100,7 +96,7 @@ ul {
|
||||
|
||||
.activity-rail {
|
||||
display: grid;
|
||||
grid-template-rows: repeat(3, minmax(42px, auto)) 1fr minmax(42px, auto);
|
||||
grid-template-rows: repeat(3, minmax(42px, auto)) minmax(0, 1fr);
|
||||
gap: 6px;
|
||||
padding: 8px;
|
||||
background: var(--rail);
|
||||
@@ -129,10 +125,6 @@ ul {
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
#explorer-toggle {
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.rail-button:hover,
|
||||
.rail-button:focus-visible,
|
||||
.rail-button.active {
|
||||
@@ -146,22 +138,6 @@ ul {
|
||||
box-shadow: inset 3px 0 0 var(--accent);
|
||||
}
|
||||
|
||||
.explorer {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-rows: auto auto minmax(0, 1fr) auto;
|
||||
gap: 8px;
|
||||
padding: 10px 14px 10px 10px;
|
||||
background: rgba(22, 25, 22, 0.98);
|
||||
border-right: 1px solid var(--line);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.explorer-collapsed {
|
||||
--explorer-width: 0px;
|
||||
grid-template-columns: var(--rail-width) 0 minmax(420px, 1fr) var(--right-width);
|
||||
}
|
||||
|
||||
.workbench-shell.is-resizing {
|
||||
cursor: col-resize;
|
||||
user-select: none;
|
||||
@@ -181,21 +157,6 @@ ul {
|
||||
opacity: 0.56;
|
||||
}
|
||||
|
||||
.explorer-resize {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 12px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.explorer-resize::before {
|
||||
top: 10px;
|
||||
bottom: 10px;
|
||||
left: 5px;
|
||||
width: 2px;
|
||||
}
|
||||
|
||||
.right-sidebar-resize {
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -223,25 +184,6 @@ ul {
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.explorer-collapsed .explorer {
|
||||
padding: 0;
|
||||
border-right: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.explorer-collapsed .explorer > * {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.explorer-collapsed .explorer {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.explorer-collapsed .explorer-resize {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.explorer-head,
|
||||
.topbar,
|
||||
.hardware-status,
|
||||
.side-workspace,
|
||||
@@ -250,15 +192,6 @@ ul {
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.explorer-head {
|
||||
min-height: 78px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
align-items: start;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
color: var(--accent-2);
|
||||
font-size: 10px;
|
||||
@@ -305,42 +238,6 @@ h3 {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.quick-actions button {
|
||||
width: 100%;
|
||||
min-height: 32px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--surface-2);
|
||||
color: var(--text);
|
||||
font-weight: 760;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.quick-actions button:hover,
|
||||
.quick-actions button:focus-visible {
|
||||
border-color: var(--line-strong);
|
||||
background: var(--surface-3);
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.tree-panel {
|
||||
min-height: 0;
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
gap: 8px;
|
||||
padding: 10px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.blockers-panel {
|
||||
max-height: 230px;
|
||||
}
|
||||
@@ -362,7 +259,6 @@ h3 {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.resource-tree,
|
||||
.compact-list,
|
||||
.conversation-list,
|
||||
.task-list,
|
||||
@@ -371,70 +267,6 @@ h3 {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.resource-tree {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.resource-tree details {
|
||||
border: 1px solid var(--line);
|
||||
background: var(--surface-2);
|
||||
}
|
||||
|
||||
.resource-tree summary,
|
||||
.tree-row {
|
||||
width: 100%;
|
||||
min-height: 31px;
|
||||
display: grid;
|
||||
grid-template-columns: 8px minmax(0, 1fr);
|
||||
gap: 7px;
|
||||
align-items: center;
|
||||
padding: 7px 8px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.resource-tree summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tree-row {
|
||||
padding-left: 22px;
|
||||
border-top: 1px solid var(--line);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tree-row:hover {
|
||||
background: rgba(212, 173, 67, 0.06);
|
||||
}
|
||||
|
||||
.tree-label,
|
||||
.tree-meta {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.tree-label {
|
||||
color: var(--text);
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tree-meta {
|
||||
grid-column: 2;
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tree-light {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: var(--dim);
|
||||
}
|
||||
|
||||
.center-workspace {
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1fr) auto;
|
||||
@@ -1741,20 +1573,12 @@ tbody tr:last-child td {
|
||||
@media (max-width: 1240px) {
|
||||
.workbench-shell {
|
||||
--rail-width: 86px;
|
||||
--explorer-width: 260px;
|
||||
--explorer-min-width: 210px;
|
||||
--explorer-max-width: 320px;
|
||||
grid-template-columns: var(--rail-width) var(--explorer-width) minmax(420px, 1fr);
|
||||
grid-template-columns: var(--rail-width) minmax(0, 1fr);
|
||||
grid-template-rows: minmax(0, 1fr) clamp(220px, 36dvh, 360px);
|
||||
}
|
||||
|
||||
.explorer-collapsed {
|
||||
--explorer-width: 0px;
|
||||
grid-template-columns: var(--rail-width) 0 minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.right-sidebar {
|
||||
grid-column: 2 / 4;
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
min-height: 0;
|
||||
padding-left: 10px;
|
||||
@@ -1768,11 +1592,8 @@ tbody tr:last-child td {
|
||||
}
|
||||
|
||||
@media (max-width: 860px) {
|
||||
.workbench-shell,
|
||||
.explorer-collapsed {
|
||||
.workbench-shell {
|
||||
--rail-width: 68px;
|
||||
--explorer-min-width: 0px;
|
||||
--explorer-max-width: 0px;
|
||||
grid-template-columns: var(--rail-width) minmax(0, 1fr);
|
||||
grid-template-rows: minmax(0, 1fr) clamp(214px, 30dvh, 280px);
|
||||
}
|
||||
@@ -1780,7 +1601,7 @@ tbody tr:last-child td {
|
||||
.activity-rail {
|
||||
grid-column: 1;
|
||||
grid-row: 1 / 3;
|
||||
grid-template-rows: repeat(3, minmax(42px, auto)) minmax(12px, 1fr) minmax(42px, auto);
|
||||
grid-template-rows: repeat(3, minmax(42px, auto)) minmax(12px, 1fr);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -1789,20 +1610,6 @@ tbody tr:last-child td {
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.explorer {
|
||||
grid-column: 2;
|
||||
grid-row: 1 / 3;
|
||||
z-index: 4;
|
||||
max-height: 100%;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
box-shadow: -1px 0 0 var(--line), 12px 0 24px rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.explorer-resize {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.right-sidebar-resize {
|
||||
display: none;
|
||||
}
|
||||
@@ -1824,10 +1631,6 @@ tbody tr:last-child td {
|
||||
grid-template-rows: auto minmax(0, auto) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.explorer-collapsed .explorer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
min-height: 0;
|
||||
gap: 8px;
|
||||
@@ -1889,8 +1692,7 @@ tbody tr:last-child td {
|
||||
|
||||
.hardware-list,
|
||||
.m3-control-grid,
|
||||
.m3-control-actions,
|
||||
.quick-actions {
|
||||
.m3-control-actions {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@@ -1939,8 +1741,7 @@ tbody tr:last-child td {
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.workbench-shell,
|
||||
.explorer-collapsed {
|
||||
.workbench-shell {
|
||||
--rail-width: 64px;
|
||||
grid-template-columns: var(--rail-width) minmax(0, 1fr);
|
||||
grid-template-rows: minmax(0, 1fr) clamp(260px, 34dvh, 320px);
|
||||
|
||||
Reference in New Issue
Block a user