推进 INI 面板 shell handoff mount DOM contract helper

This commit is contained in:
2026-06-15 12:12:01 +08:00
parent 22ca6ced3f
commit 94b44c0e73
9 changed files with 331 additions and 29 deletions

View File

@@ -143,6 +143,8 @@ export function createIniPanelLaunchApiManifest(entries = getVisibleIniPanelEntr
"getShellSessionHandoffPackageMountState",
"getShellSessionHandoffPackageMountDisplayViewModel",
"getShellSessionHandoffPackageMountRenderState",
"getShellSessionHandoffMountDomContract",
"getShellSessionHandoffMountDomReadiness",
"renderShellSessionHandoffMountState",
"isSupportedShellSessionHandoffPackage",
"getLauncherLinks",
@@ -1079,21 +1081,85 @@ export function createIniPanelShellSessionHandoffPackageMountRenderState(
};
}
export function createIniPanelShellSessionHandoffMountDomContract({
rowDatasetPrefix = "handoffMount",
selectors = {},
} = {}) {
const mountSelector = selectors.mount ?? "[data-handoff-mount]";
const statusSelector = selectors.status ?? "[data-handoff-mount-status]";
const rowsSelector = selectors.rows ?? "[data-handoff-mount-rows]";
return {
apiName: "ini-panel-shell-session-handoff-mount-dom-contract",
contractVersion: 1,
requiredNodeIds: ["mount", "status", "rows"],
selectors: {
mount: mountSelector,
status: statusSelector,
rows: rowsSelector,
},
mountDatasetKeys: ["handoffPhase", "handoffReady", "handoffScope"],
rowDatasetPrefix,
rowDatasetKeys: {
term: [`${rowDatasetPrefix}Row`, `${rowDatasetPrefix}Status`],
detail: [`${rowDatasetPrefix}Value`, `${rowDatasetPrefix}Status`],
},
renderResultFields: ["rendered", "statusLine", "rowCount", "rowIds", "dataset"],
};
}
export function createIniPanelShellSessionHandoffMountDomReadiness({
rowDatasetPrefix = "handoffMount",
contract = createIniPanelShellSessionHandoffMountDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {}) {
const checks = {
document: Boolean(documentRef?.createElement),
mount: Boolean(mountNode),
status: Boolean(statusNode),
rows: Boolean(rowsNode),
rowDatasetPrefix: Boolean(contract.rowDatasetPrefix),
};
const missing = Object.entries(checks)
.filter(([, passed]) => !passed)
.map(([name]) => name);
const ready = missing.length === 0;
return {
apiName: "ini-panel-shell-session-handoff-mount-dom-readiness",
readinessVersion: 1,
phase: ready ? "ready" : "blocked",
ready,
missing,
checks,
contract,
};
}
export function renderIniPanelShellSessionHandoffMountState(
renderState = createIniPanelShellSessionHandoffPackageMountRenderState(),
{
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.("[data-handoff-mount]"),
statusNode = documentRef?.querySelector?.("[data-handoff-mount-status]"),
rowsNode = documentRef?.querySelector?.("[data-handoff-mount-rows]"),
rowDatasetPrefix = "handoffMount",
contract = createIniPanelShellSessionHandoffMountDomContract({ rowDatasetPrefix }),
documentRef = globalThis.document,
mountNode = documentRef?.querySelector?.(contract.selectors.mount),
statusNode = documentRef?.querySelector?.(contract.selectors.status),
rowsNode = documentRef?.querySelector?.(contract.selectors.rows),
} = {},
) {
if (!documentRef?.createElement) {
throw new Error("A DOM document is required to render shell handoff mount state.");
}
if (!mountNode || !statusNode || !rowsNode) {
throw new Error("Mount, status, and rows nodes are required to render shell handoff mount state.");
const readiness = createIniPanelShellSessionHandoffMountDomReadiness({
contract,
documentRef,
mountNode,
statusNode,
rowsNode,
});
if (!readiness.ready) {
throw new Error(
`Shell handoff mount DOM is not ready: ${readiness.missing.join(", ")}`,
);
}
mountNode.dataset.handoffPhase = renderState?.dataset?.handoffPhase ?? "blocked";
@@ -1103,14 +1169,15 @@ export function renderIniPanelShellSessionHandoffMountState(
rowsNode.replaceChildren();
const rows = Array.isArray(renderState?.rows) ? renderState.rows : [];
const effectiveRowDatasetPrefix = contract.rowDatasetPrefix;
for (const row of rows) {
const term = documentRef.createElement("dt");
term.dataset[`${rowDatasetPrefix}Row`] = row.id;
term.dataset[`${rowDatasetPrefix}Status`] = row.status;
term.dataset[`${effectiveRowDatasetPrefix}Row`] = row.id;
term.dataset[`${effectiveRowDatasetPrefix}Status`] = row.status;
term.textContent = row.label;
const detail = documentRef.createElement("dd");
detail.dataset[`${rowDatasetPrefix}Value`] = row.id;
detail.dataset[`${rowDatasetPrefix}Status`] = row.status;
detail.dataset[`${effectiveRowDatasetPrefix}Value`] = row.id;
detail.dataset[`${effectiveRowDatasetPrefix}Status`] = row.status;
detail.textContent = row.value;
rowsNode.append(term, detail);
}
@@ -1191,6 +1258,7 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries
const shellSessionHandoffPackageMountRenderState = createIniPanelShellSessionHandoffPackageMountRenderState(
shellSessionHandoffPackageMountDisplayViewModel,
);
const shellSessionHandoffMountDomContract = createIniPanelShellSessionHandoffMountDomContract();
return {
pages: entries.map(({ id, href, title }) => ({
id,
@@ -1218,6 +1286,7 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries
shellSessionHandoffPackageMountState,
shellSessionHandoffPackageMountDisplayViewModel,
shellSessionHandoffPackageMountRenderState,
shellSessionHandoffMountDomContract,
launchApiSchema: createIniPanelLaunchApiSchema(),
launchApiManifest: createIniPanelLaunchApiManifest(entries),
isSupportedShellIntegrationManifest: isSupportedIniPanelShellIntegrationManifest,
@@ -1240,6 +1309,8 @@ export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries
createShellSessionHandoffPackageMountState: createIniPanelShellSessionHandoffPackageMountState,
createShellSessionHandoffPackageMountDisplayViewModel: createIniPanelShellSessionHandoffPackageMountDisplayViewModel,
createShellSessionHandoffPackageMountRenderState: createIniPanelShellSessionHandoffPackageMountRenderState,
createShellSessionHandoffMountDomContract: createIniPanelShellSessionHandoffMountDomContract,
createShellSessionHandoffMountDomReadiness: createIniPanelShellSessionHandoffMountDomReadiness,
renderShellSessionHandoffMountState: renderIniPanelShellSessionHandoffMountState,
isSupportedShellSessionHandoffPackage: isSupportedIniPanelShellSessionHandoffPackage,
controlPage: {