feat: add configurable operator appearance and layout

This commit is contained in:
2026-07-23 11:14:10 +09:00
parent cf117de144
commit f43483533a
34 changed files with 5942 additions and 740 deletions

View File

@@ -78,6 +78,7 @@ const evidence = {
inferredFromAuthoritativeState: true,
nativeRoute: "LegacyReadyIntent => _controller.Current"
},
preGuardStartupSettle: null,
blockedMessages: [],
observedMessagesAfterGuard: []
},
@@ -162,6 +163,66 @@ async function evaluate(expression) {
return response.result?.value;
}
async function waitForSettledStartup() {
const settleDeadline = Math.min(Date.now() + 10_000, deadline);
let stableSignature = null;
let stableSince = 0;
let lastSample = null;
while (Date.now() < settleDeadline) {
lastSample = await evaluate(`(() => {
const currentMenu = Array.from(document.querySelectorAll(
"#workspace-navigation .workspace-nav-item[aria-current='page']"));
const region = document.getElementById("workspace-region");
return {
readyState: document.readyState,
bodyBusy: document.body?.getAttribute("aria-busy") || null,
bridgeReady: typeof window.chrome?.webview?.postMessage === "function",
connectionText: (document.getElementById("playout-connection-state")?.textContent || "").trim(),
playoutStatus: (document.getElementById("playout-status")?.textContent || "").trim(),
activeWorkspace: region?.dataset?.activeWorkspace || null,
activeMarketTab: region?.dataset?.activeMarketTab || null,
startWorkspace: document.body?.dataset?.startWorkspace || null,
workspaceTitle: (document.getElementById("workspace-title")?.textContent || "").trim(),
currentMenu: currentMenu.map(element => element.id || element.dataset?.tabId || null)
};
})()`);
const ready = lastSample?.readyState === "complete" &&
lastSample.bodyBusy === "false" && lastSample.bridgeReady === true &&
lastSample.connectionText === "DRY RUN" &&
lastSample.playoutStatus.startsWith("IDLE") &&
["stock", "catalog", "settings"].includes(lastSample.activeWorkspace) &&
["stockCut", "lastWorkspace"].includes(lastSample.startWorkspace) &&
lastSample.workspaceTitle.length > 0 && lastSample.currentMenu.length === 1;
const signature = ready ? JSON.stringify({
activeWorkspace: lastSample.activeWorkspace,
activeMarketTab: lastSample.activeMarketTab,
startWorkspace: lastSample.startWorkspace,
workspaceTitle: lastSample.workspaceTitle,
currentMenu: lastSample.currentMenu
}) : null;
if (signature && signature === stableSignature) {
if (Date.now() - stableSince >= 400) {
return {
stableMilliseconds: Date.now() - stableSince,
activeWorkspace: lastSample.activeWorkspace,
activeMarketTab: lastSample.activeMarketTab,
startWorkspace: lastSample.startWorkspace,
workspaceTitle: lastSample.workspaceTitle,
currentMenu: lastSample.currentMenu
};
}
} else {
stableSignature = signature;
stableSince = signature ? Date.now() : 0;
}
await sleep(75);
}
if (Date.now() >= deadline) {
fail("OUTCOME_UNKNOWN", "Startup did not settle before the observation deadline; no guard was installed.");
}
fail("KNOWN_FAILURE", `Startup did not reach a stable DryRun workspace: ${JSON.stringify(lastSample)}`);
}
async function installNoIntentGuard() {
const installed = await evaluate(`(() => {
const bridge = window.chrome?.webview;
@@ -212,6 +273,7 @@ async function readShell() {
bounds.left < document.documentElement.clientWidth &&
bounds.top < document.documentElement.clientHeight &&
bounds.display !== "none" && bounds.visibility !== "hidden");
const operatorShell = document.querySelector(".operator-shell");
const workspaceRegion = document.getElementById("workspace-region");
const navigationToggle = document.getElementById("workspace-nav-toggle");
const stockTab = document.getElementById("workspace-stock-tab");
@@ -220,8 +282,11 @@ async function readShell() {
const stockWorkspace = document.getElementById("stock-workspace");
const catalogWorkspace = document.getElementById("catalog-workspace");
const settingsWorkspace = document.getElementById("settings-workspace");
const splitter = document.getElementById("workspace-splitter");
const schedule = document.querySelector(".playlist-panel");
const shellBounds = rectangle(operatorShell);
const workspaceBounds = rectangle(workspaceRegion);
const splitterBounds = rectangle(splitter);
const scheduleBounds = rectangle(schedule);
const scheduleCenter = scheduleBounds ? {
x: scheduleBounds.left + scheduleBounds.width / 2,
@@ -249,12 +314,15 @@ async function readShell() {
},
layout: {
activeWorkspace: workspaceRegion?.dataset?.activeWorkspace || null,
navigationAriaExpanded: navigationToggle?.getAttribute("aria-expanded") || null,
navigationCollapsedClass: workspaceRegion?.classList.contains("nav-collapsed") === true,
navigationExpanded: navigationToggle?.getAttribute("aria-expanded") === "true" &&
workspaceRegion?.classList.contains("nav-collapsed") !== true,
stockTabCurrent: stockTab?.getAttribute("aria-current") || null,
marketTabCount: marketTabs.length,
marketTabsCurrent: marketTabs.map(button => ({
tabId: button.dataset.tabId,
label: (button.querySelector(".workspace-nav-label")?.textContent || "").trim(),
current: button.getAttribute("aria-current") || null,
hasIcon: Boolean(button.querySelector("svg")),
hasLabel: Boolean(button.querySelector(".workspace-nav-label"))
@@ -265,18 +333,42 @@ async function readShell() {
stockWorkspaceVisible: isVisible(stockWorkspace, rectangle(stockWorkspace)) &&
stockWorkspace?.getAttribute("aria-hidden") === "false" &&
stockWorkspace?.hasAttribute("inert") !== true,
stockWorkspaceHidden: stockWorkspace?.hidden === true &&
stockWorkspace?.getAttribute("aria-hidden") === "true" &&
stockWorkspace?.hasAttribute("inert") === true,
catalogWorkspaceVisible: isVisible(catalogWorkspace, rectangle(catalogWorkspace)) &&
catalogWorkspace?.getAttribute("aria-hidden") === "false" &&
catalogWorkspace?.hasAttribute("inert") !== true,
catalogWorkspaceHidden: catalogWorkspace?.hidden === true &&
catalogWorkspace?.getAttribute("aria-hidden") === "true" &&
catalogWorkspace?.hasAttribute("inert") === true,
settingsWorkspaceVisible: isVisible(settingsWorkspace, rectangle(settingsWorkspace)) &&
settingsWorkspace?.getAttribute("aria-hidden") === "false" &&
settingsWorkspace?.hasAttribute("inert") !== true,
settingsWorkspaceHidden: settingsWorkspace?.hidden === true &&
settingsWorkspace?.getAttribute("aria-hidden") === "true" &&
settingsWorkspace?.hasAttribute("inert") === true,
workspaceTitle: text("workspace-title"),
shell: shellBounds,
workspace: workspaceBounds,
splitter: splitterBounds,
splitterState: splitter ? {
role: splitter.getAttribute("role"),
orientation: splitter.getAttribute("aria-orientation"),
controls: splitter.getAttribute("aria-controls"),
valueMinimum: Number(splitter.getAttribute("aria-valuemin")),
valueMaximum: Number(splitter.getAttribute("aria-valuemax")),
valueNow: Number(splitter.getAttribute("aria-valuenow")),
tabIndex: splitter.tabIndex
} : null,
schedule: scheduleBounds,
workspaceToScheduleWidthRatio: workspaceBounds && scheduleBounds &&
scheduleBounds.width > 0
? workspaceBounds.width / scheduleBounds.width
: null,
scheduleShare: shellBounds && scheduleBounds && shellBounds.width > 0
? scheduleBounds.width / shellBounds.width
: null,
scheduleVisible: isVisible(schedule, scheduleBounds),
scheduleHitTestVisible: Boolean(scheduleHit &&
(scheduleHit === schedule || schedule?.contains(scheduleHit)))
@@ -298,22 +390,53 @@ function assertShell(shell) {
fail("KNOWN_FAILURE", "The package shell did not remain rendered DryRun/IDLE with zero guarded native intents.");
}
const layout = shell.layout;
if (!layout || layout.activeWorkspace !== "stock" ||
layout.navigationExpanded !== true ||
layout.stockTabCurrent !== "page" || layout.settingsTabCurrent !== "false" ||
layout.marketTabCount !== 10 ||
layout.marketTabsCurrent.some(tab => tab.current !== "false" ||
tab.hasIcon !== true || tab.hasLabel !== true) ||
layout.currentMenuItemCount !== 1 ||
layout.stockWorkspaceVisible !== true || layout.catalogWorkspaceHidden !== true ||
layout.settingsWorkspaceHidden !== true) {
fail("KNOWN_FAILURE", "The package did not render the expanded navigation with stock as the sole initial workspace.");
const currentMarkets = layout?.marketTabsCurrent?.filter(tab => tab.current === "page") || [];
const navigationExpansionConsistent = layout &&
((layout.navigationAriaExpanded === "true" && layout.navigationCollapsedClass === false) ||
(layout.navigationAriaExpanded === "false" && layout.navigationCollapsedClass === true));
const commonNavigationOk = layout && navigationExpansionConsistent &&
layout.marketTabCount === 10 && layout.currentMenuItemCount === 1 &&
layout.marketTabsCurrent.every(tab => tab.hasIcon === true && tab.hasLabel === true);
const stockWorkspaceOk = layout?.activeWorkspace === "stock" &&
layout.stockTabCurrent === "page" && layout.settingsTabCurrent === "false" &&
currentMarkets.length === 0 && layout.stockWorkspaceVisible === true &&
layout.catalogWorkspaceHidden === true && layout.settingsWorkspaceHidden === true &&
layout.workspaceTitle === "종목·컷";
const settingsWorkspaceOk = layout?.activeWorkspace === "settings" &&
layout.stockTabCurrent === "false" && layout.settingsTabCurrent === "page" &&
currentMarkets.length === 0 && layout.stockWorkspaceHidden === true &&
layout.catalogWorkspaceHidden === true && layout.settingsWorkspaceVisible === true &&
layout.workspaceTitle === "설정";
const catalogWorkspaceOk = layout?.activeWorkspace === "catalog" &&
layout.stockTabCurrent === "false" && layout.settingsTabCurrent === "false" &&
currentMarkets.length === 1 && layout.stockWorkspaceHidden === true &&
layout.catalogWorkspaceVisible === true && layout.settingsWorkspaceHidden === true &&
layout.workspaceTitle === currentMarkets[0]?.label;
if (!commonNavigationOk ||
[stockWorkspaceOk, settingsWorkspaceOk, catalogWorkspaceOk].filter(Boolean).length !== 1) {
fail("KNOWN_FAILURE", "The package did not render one internally consistent initial workspace and menu selection.");
}
const splitterState = layout.splitterState;
if (layout.scheduleVisible !== true || layout.scheduleHitTestVisible !== true ||
!layout.shell || !layout.workspace || !layout.splitter || !layout.schedule ||
!Number.isFinite(layout.workspaceToScheduleWidthRatio) ||
layout.workspaceToScheduleWidthRatio < 1.60 ||
layout.workspaceToScheduleWidthRatio > 1.90) {
fail("KNOWN_FAILURE", "The package did not render a visible right schedule beside the approximately 1.75:1 workspace layout.");
layout.workspaceToScheduleWidthRatio < 1.05 ||
layout.workspaceToScheduleWidthRatio > 1.95 ||
!Number.isFinite(layout.scheduleShare) ||
layout.scheduleShare < 0.33 || layout.scheduleShare > 0.49 ||
splitterState?.role !== "separator" || splitterState.orientation !== "vertical" ||
splitterState.controls !== "workspace-region" || splitterState.tabIndex !== 0 ||
!Number.isFinite(splitterState.valueMinimum) || splitterState.valueMinimum < 34 ||
!Number.isFinite(splitterState.valueMaximum) || splitterState.valueMaximum > 48 ||
!Number.isFinite(splitterState.valueNow) ||
splitterState.valueNow < splitterState.valueMinimum ||
splitterState.valueNow > splitterState.valueMaximum ||
Math.abs(layout.workspace.right - layout.splitter.left) > 1.5 ||
Math.abs(layout.splitter.right - layout.schedule.left) > 1.5 ||
Math.abs(layout.schedule.right - layout.shell.right) > 1.5 ||
Math.abs(layout.splitter.top - layout.shell.top) > 1.5 ||
Math.abs(layout.splitter.bottom - layout.shell.bottom) > 1.5) {
fail("KNOWN_FAILURE", "The package did not render a visible right schedule beside the adjustable workspace layout.");
}
for (const [name, control] of Object.entries(shell.controls || {})) {
if (!control?.present) fail("KNOWN_FAILURE", `Required shell control ${name} is missing.`);
@@ -335,6 +458,7 @@ try {
await connect(target);
await rpc("Runtime.enable");
await rpc("Page.enable");
evidence.safety.preGuardStartupSettle = await waitForSettledStartup();
await installNoIntentGuard();
await sleep(500);
evidence.shell = await readShell();