feat: 完善 N7 智能座舱交互与控制链路

This commit is contained in:
vee
2026-09-07 15:34:59 +08:00
parent 32d179143f
commit c96970f068
100 changed files with 30088 additions and 1174 deletions
@@ -635,9 +635,12 @@ def test_left_sidebar_extracts_speech_and_peripheral_control_commands() -> None:
}
def test_interactive_console_keeps_actions_inside_composer_and_collapsed_rails_aligned() -> None:
def test_interactive_console_uses_one_top_right_icon_dock_for_all_five_panels() -> None:
console = source("console-v2.js")
assistant = source("assistant-chat.js")
energy = source("energy-monitor.js")
styles = source("console-v2.css")
extension = source("self-extension.js")
assert 'const resetButton = $("#newConversation", originalForm);' in console
assert 'const runButton = $("#runButton", originalForm);' in console
@@ -647,10 +650,38 @@ def test_interactive_console_keeps_actions_inside_composer_and_collapsed_rails_a
assert ".v2-request-form .command-composer" in styles
assert "overflow: hidden;" in styles
assert "transform: translateY(-50%);" in styles
assert '.joint-twin-shell[data-open-panels="none"]' in styles
assert "grid-template-columns: 48px minmax(520px, 1fr) 48px;" in styles
assert "writing-mode: horizontal-tb;" in styles
assert "writing-mode: vertical-rl;" in styles
assert console.index('id="v2DevicePanel"') < console.index('id="v2ExtensionPanel"')
assert 'class="v2-side-status-stack"' in console
assert 'class="v2-panel-control-dock"' in console
assert 'panelDock.append(sidebarToggle)' in console
assert 'panelDock.append(deviceToggle, extensionToggle)' in console
assert 'class="v2-side-panel-dock"' not in console
assert 'deviceToggle.dataset.panelToggle = "devices"' in console
assert 'extensionToggle.dataset.panelToggle = "extensions"' in console
assert 'data-lucide="car-front"' in console
assert 'data-lucide="usb"' in console
assert 'data-lucide="bot-message-square"' in assistant
assert '($("#v2PanelControlDock") || document.body).append(trigger)' in assistant
assert 'trigger.className = "assistant-drawer-toggle v2-panel-control-button"' in assistant
assert 'trigger.className = "energy-monitor-toggle v2-panel-control-button"' in energy
assert '$("#v2PanelControlDock").append(trigger)' in energy
assert 'trigger.setAttribute("aria-controls", "energyMonitorLayer")' in energy
assert "panel.hidden = !visible;" in console
assert 'rightRail.hidden = collapsed;' in console
assert 'rightRail.toggleAttribute("inert", collapsed)' in console
assert "data-panel-toggle" not in extension
assert 'setSidePanelVisibility("devices", false)' in console
assert 'setSidePanelVisibility("extensions", false)' in console
assert "setDrawerOpen(true)" in assistant
assert ".v2-panel-control-dock" in styles
assert "#v2PanelControlDock > .v2-panel-control-button" in styles
assert '.v2-panel-control-button[aria-pressed="true"]' in styles
assert "background: var(--primary);" in styles
assert "width: 38px !important;" in styles
final_styles = styles.split("/* AI assistant light-blue final override", 1)[1]
assert 'data-open-panels="none"' in final_styles
assert "grid-template-columns: minmax(0, 1fr);" in final_styles
assert ".v2-unified-right-rail[hidden]" in final_styles
def test_left_sync_rail_can_collapse_and_log_aligns_with_lower_workspace() -> None:
@@ -660,8 +691,14 @@ def test_left_sync_rail_can_collapse_and_log_aligns_with_lower_workspace() -> No
assert 'make("section", "v2-sync-sidebar"' in console
assert '<div class="v2-sync-sidebar-overview">' in console
assert "syncOverview?.append(sidebarFooter);" in console
assert '$(".v2-sync-sidebar-heading", syncSidebar)?.append(sidebarToggle);' in console
assert '$(".v2-sync-sidebar-heading", syncSidebar)?.append(sidebarToggle);' not in console
assert 'commandPanel.id = "v2SidebarPanel"' in console
assert 'sidebarToggle.setAttribute("aria-controls", commandPanel.id)' in console
assert "sidebarCollapsed: true" in console
assert 'sidebar.toggleAttribute("inert", ui.sidebarCollapsed)' in console
assert 'button.setAttribute("aria-pressed", String(expanded))' in console
assert "function syncSidebarLogBaseline()" in console
assert "if (!sidebar || !lowerPanel || ui.sidebarCollapsed) return;" in console
assert "const requiredOverviewHeight = overviewRect" in console
assert "overviewBottom - overviewRect.top + overviewPaddingBottom + 10" in console
assert 'sidebar.style.setProperty("--v2-sync-log-top"' in console
@@ -676,8 +713,10 @@ def test_left_sync_rail_can_collapse_and_log_aligns_with_lower_workspace() -> No
assert "align-self: start;" in styles
assert "overflow: visible;" in styles
assert ".v2-sync-sidebar > .v2-sync-log" in styles
assert "grid-template-columns: 64px minmax(0, 1fr);" in styles
assert ".v2-sidebar-collapsed #v2SidebarToggle" in styles
final_styles = styles.split("/* AI assistant light-blue final override", 1)[1]
assert "grid-template-columns: 0 minmax(0, 1fr);" in final_styles
assert ".command-panel.v2-sidebar" in final_styles
assert "visibility: hidden;" in final_styles
def test_twin_device_status_popover_has_clear_close_paths() -> None:
@@ -696,6 +735,7 @@ def test_twin_device_status_popover_has_clear_close_paths() -> None:
def test_twin_device_hotspots_use_responsive_hover_and_leader_labels() -> None:
app = source("app.js")
console = source("console-v2.js")
styles = source("console-v2.css")
@@ -713,12 +753,61 @@ def test_twin_device_hotspots_use_responsive_hover_and_leader_labels() -> None:
assert ".v2-twin-hotspots button::before" in styles
assert ".v2-twin-hotspots button::after" in styles
assert ".v2-twin-hotspot-label" in styles
assert "--v2-hotspot-label-x: -115px" in styles
assert "--v2-hotspot-line-angle: -150deg" in styles
assert "width: 94px;" in styles
assert "max-width: 94px;" in styles
assert "box-sizing: border-box;" in styles
assert "text-overflow: ellipsis;" in styles
assert "function twinDeviceWorldAnchor(" in app
assert "function positionTwinDeviceHotspots()" in app
assert "projectedHotspot.copy(hotspotWorldAnchor).project(camera)" in app
assert 'button.dataset.anchorSource = "projected-3d"' in app
assert 'hotspotLayer.dataset.anchorLayout = "projected-3d"' in app
assert "clampLabelCoordinate(" in app
assert "positionTwinDeviceHotspots();" in app
assert "function positionTwinDevicePopover(" in console
assert "function twinPopoverIntersectionArea(" in console
assert 'Object.keys(availableSpace).sort' in console
assert 'popover.dataset.anchorDevice = deviceId' in console
assert 'popover.dataset.placement = best.placement' in console
assert 'popover.dataset.shadeOverlap = String(Math.round(best.shadeOverlap))' in console
assert 'popover.dataset.labelOverlap = String(Math.round(best.labelOverlap))' in console
assert 'popover.dataset.layoutSignature === geometrySignature' in console
assert 'window.CognitiveAgentConsoleV2?.positionTwinDevicePopover?.();' in app
popover_styles = styles.split(".console-v2 .v2-twin-device-popover {", 1)[1].split("}", 1)[0]
assert "top: 52px;" not in popover_styles
assert "left: 50%;" not in popover_styles
assert "transform: translateX(-50%);" not in popover_styles
assert 'width: min(280px, calc(100% - 24px));' in styles
assert 'repeat(auto-fit, minmax(44px, 1fr))' in styles
for device_id in (
"windows", "shade", "driver", "passenger",
"reading", "ambient", "headlight", "climate",
):
assert f"{device_id}: {{ normalized:" in app
assert '.v2-twin-device-popover[data-pinned="false"]' in styles
assert "pointer-events: none;" in styles
def test_vehicle_twin_keeps_whole_car_centered_and_removes_axis_callouts() -> None:
html = source("index.html")
app = source("app.js")
styles = source("console-v2.css")
assert 'twin.view.targetX = 0;' in app
assert 'twin.view.yaw = -0.62;' in app
assert 'canvas.dataset.framing = "whole-vehicle-centered"' in app
assert "1.28 / Math.max(0.64, camera.aspect)" in app
assert 'class="twin-callout-layer"' not in html
assert 'class="twin-callout' not in html
assert "data-callout=" not in html
assert "function positionTwinCallouts()" not in app
assert "renderTwinCalloutValues" not in app
assert ".joint-twin-shell > .twin-band" in styles
assert "container-type: inline-size;" in styles
assert "@container (max-width: 420px)" in styles
assert "grid-template-columns: minmax(0, 1fr);" in styles
def test_validated_can_commands_survive_same_task_running_poll_and_keep_error_detail() -> None:
console = source("console-v2.js")
app = source("app.js")
@@ -733,3 +822,118 @@ def test_validated_can_commands_survive_same_task_running_poll_and_keep_error_de
assert "failed_or_blocked: \"失败或已阻止\"" in console
assert "requestError.outcome = phaseEvent.outcome;" in app
assert "requestError.detail = phaseEvent.detail;" in app
def test_compact_mode_strip_supports_wheel_drag_and_keyboard_navigation() -> None:
app = source("app.js")
styles = source("console-v2.css")
scroller = app.split("function installModeChipScroller()", 1)[1].split(
"function populateModeChips", 1
)[0]
assert 'group.dataset.modeScrollerBound = "true"' in scroller
assert "group.tabIndex = 0" in scroller
assert "滚轮、拖动或方向键横向浏览" in scroller
assert 'group.addEventListener("wheel"' in scroller
assert "Math.abs(event.deltaX) > Math.abs(event.deltaY)" in scroller
assert "scrollBy(dominantDelta * multiplier)" in scroller
assert "event.preventDefault()" in scroller
assert "{ passive: false }" in scroller
assert 'group.addEventListener("pointerdown"' in scroller
assert 'event.pointerType !== "mouse"' in scroller
assert "group.setPointerCapture?.(pointerId)" in scroller
assert 'group.addEventListener("pointermove"' in scroller
assert "Math.abs(distance) < 5" in scroller
assert 'group.classList.add("is-dragging")' in scroller
assert 'group.addEventListener("pointerup", finishPointerDrag)' in scroller
assert 'group.addEventListener("pointercancel", finishPointerDrag)' in scroller
assert "event.stopImmediatePropagation()" in scroller
assert 'group.addEventListener("keydown"' in scroller
for key in ("ArrowLeft", "ArrowRight", "PageUp", "PageDown", "Home", "End"):
assert f'event.key === "{key}"' in scroller
assert "new MutationObserver" in scroller
assert 'new ResizeObserver(updateScrollState).observe(group)' in scroller
assert "installModeChipScroller();" in app
mode_strip_styles = styles.split("/* Compact function-mode strip", 1)[1]
assert "overflow-x: auto" in mode_strip_styles
assert "overflow-y: hidden" in mode_strip_styles
assert "touch-action: pan-x" in mode_strip_styles
assert "cursor: grab" in mode_strip_styles
assert ".mode-chips:focus-visible" in mode_strip_styles
assert ".mode-chips.is-dragging" in mode_strip_styles
assert "cursor: grabbing" in mode_strip_styles
def test_assistant_mode_preview_groups_validated_actions_for_the_card() -> None:
node = shutil.which("node")
if node is None:
return
assistant = source("assistant-chat.js")
helper_start = assistant.index(" function modeDraftRows")
helper_end = assistant.index("\n\n async function saveModeProfileDraft", helper_start)
helper = assistant[helper_start:helper_end]
draft = {
"name": "观影模式",
"instruction": "遮阳帘关闭,车窗关闭,副驾座椅调整为零重力模式,灯光全部关闭",
}
actions = [
{
"domain": "roof",
"capability": "roof.set_shade_motion",
"target": {"zone": "vehicle"},
"parameters": {"motion": "close"},
},
{
"domain": "window",
"capability": "window.set_motion",
"target": {"zone": "all"},
"parameters": {"motion": "close"},
},
{
"domain": "seat",
"capability": "seat.set_longitudinal_position",
"target": {"zone": "front_right", "instance": "passenger"},
"parameters": {"position_pct": 100},
},
{
"domain": "seat",
"capability": "seat.set_backrest_recline",
"target": {"zone": "front_right", "instance": "passenger"},
"parameters": {"position_pct": 81, "angle_deg": 150},
},
{
"domain": "interior_light",
"capability": "interior_light.set_indoor_power",
"target": {"zone": "vehicle"},
"parameters": {"state": "off"},
},
{
"domain": "ambient_light",
"capability": "ambient_light.set_power",
"target": {"zone": "vehicle"},
"parameters": {"state": "off"},
},
]
script = (
f"{helper}\n"
f"const draft = {json.dumps(draft, ensure_ascii=False)};\n"
f"const actions = {json.dumps(actions, ensure_ascii=False)};\n"
"process.stdout.write(JSON.stringify(modeDraftRows(draft, actions)));"
)
completed = subprocess.run(
[node, "-e", script],
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
assert json.loads(completed.stdout) == [
{"label": "遮阳帘", "value": "关闭", "icon": "blinds", "order": 0},
{"label": "全部车窗", "value": "关闭", "icon": "panels-top-left", "order": 6},
{"label": "副驾座椅", "value": "零重力模式", "icon": "armchair", "order": 11},
{"label": "座舱灯光", "value": "全部关闭", "icon": "lightbulb-off", "order": 24},
]