@@ -757,12 +757,12 @@
-
已学习红外信号按设备 ID 分组;对象名称用于自然语言识别和调试,动作请包含结果值,如“切换为蓝色”
+
JSON 调试结果
等待操作
@@ -4085,12 +4085,12 @@
- ${escapeHtml(nfcTitle)}${escapeHtml(nfcNote)}
${nfcCapabilities.length ? `
${nfcCapabilities.slice(0, 6).map((capability) => `${escapeHtml(capability.name)}`).join("")}
` : ""}
+ ${escapeHtml(nfcTitle)}${nfcPet ? "" : `
${escapeHtml(nfcNote)}
`}${nfcCapabilities.length ? `
${nfcCapabilities.slice(0, 6).map((capability) => `${escapeHtml(capability.name)}`).join("")}
` : ""}
-
${gatewayLabel}${escapeHtml(gatewayNote)}
+
${gatewayLabel}
${String(health.transport || "GSCU").toUpperCase()}
@@ -4098,7 +4098,7 @@
${nfcPet ? renderDetectedPetCard(nfcPet) : ""}
${devices.length ? devices.map((device) => `
-
+
${device.actions.slice(0, 8).map((action) => `${escapeHtml(action)}`).join("")}
`).join("") : nfcPet ? "" : `
diff --git a/apps/web-console/service-worker.js b/apps/web-console/service-worker.js
index 6844702..d5ec45f 100644
--- a/apps/web-console/service-worker.js
+++ b/apps/web-console/service-worker.js
@@ -1,4 +1,4 @@
-const CACHE_VERSION = "cognitive-agent-pad-v302";
+const CACHE_VERSION = "cognitive-agent-pad-v305";
const SHELL_CACHE = `${CACHE_VERSION}-shell`;
const RUNTIME_CACHE = `${CACHE_VERSION}-runtime`;
const APP_SHELL = [
@@ -6,21 +6,21 @@ const APP_SHELL = [
"/index.html",
"/styles.css?v=dual-seat-23",
"/network.js?v=android-pad-2",
- "/app.js?v=dual-seat-139",
+ "/app.js?v=dual-seat-140",
"/gesture-control.js?v=android-pad-9",
"/pwa.js?v=android-pad-3",
"/console-v2.css?v=delivery-state-69",
"/console-v2.js?v=local-first-70",
- "/self-extension.css?v=pet-settings-49",
- "/self-extension.js?v=pet-settings-49",
+ "/self-extension.css?v=pet-settings-52",
+ "/self-extension.js?v=pet-settings-52",
"/speech-input.css?v=sensevoice-7",
"/speech-input.js?v=sensevoice-13",
"/speech-audio-worklet.js?v=sensevoice-5",
- "/assistant-chat.css?v=codex-assistant-28",
- "/assistant-chat.js?v=codex-assistant-32",
+ "/assistant-chat.css?v=codex-assistant-29",
+ "/assistant-chat.js?v=codex-assistant-33",
"/energy-monitor.css?v=energy-monitor-4",
"/energy-monitor.js?v=energy-monitor-4",
- "/ui-consistency.css?v=spatial-ui-4",
+ "/ui-consistency.css?v=spatial-ui-5",
"/codepen-glass-theme.css?v=creative-cloud-2",
"/vue-ui.js?v=vue-presentation-3",
"/assets/pets/self-extension-cat.png?v=3",
diff --git a/apps/web-console/ui-consistency.css b/apps/web-console/ui-consistency.css
index 7e9a0ad..06e381f 100644
--- a/apps/web-console/ui-consistency.css
+++ b/apps/web-console/ui-consistency.css
@@ -2,12 +2,16 @@
Keep geometry consistent across legacy modules without changing their
domain-specific colors, content density, or interaction behavior.
- Hierarchy: control 10px -> item 12px -> section 16px -> panel 18px ->
- modal 22px. All modules consume the same semantic color tokens. */
+ Hierarchy: control 10px -> card 13px -> section 15px -> panel 16px ->
+ modal 20px. All modules consume the same semantic color tokens. */
:root {
--ui-control-height-compact: 30px;
--ui-control-height: 38px;
--ui-control-size-icon: 36px;
+ --ui-font-card-title: 13px;
+ --ui-font-section-title: 15px;
+ --ui-font-panel-title: 16px;
+ --ui-inline-action-width: 88px;
--ui-radius-control-inner: calc(var(--radius) - 2px);
--ui-radius-control: var(--radius);
--ui-radius-item: calc(var(--radius) + 2px);
@@ -27,6 +31,43 @@
--ui-focus-ring: 0 0 0 3px color-mix(in oklch, var(--ring) 22%, transparent);
}
+/* One readable type scale prevents large legacy headings and tiny newer card
+ titles from competing inside the same workspace. */
+.console-v2 :is(
+ .v2-operation-heading h2,
+ .joint-twin-shell .twin-heading h3,
+ .v2-device-sync-board > header h3,
+ .v2-extension-sync-heading h3,
+ .v2-sync-sidebar-heading h2,
+ .workspace-panel-title h3,
+ .config-panel-heading h3
+) {
+ font-size: var(--ui-font-panel-title) !important;
+ line-height: 1.25;
+ letter-spacing: -0.02em;
+}
+
+.console-v2 :is(
+ .v2-pet-card-region > header h4,
+ .v2-gscu-actions > header strong,
+ .v2-gscu-command-catalog > header strong
+) {
+ font-size: var(--ui-font-section-title) !important;
+ line-height: 1.3;
+}
+
+.console-v2 :is(
+ .v2-device-card-focus strong,
+ .v2-nfc-inline strong,
+ .v2-extension-gateway strong,
+ .v2-extension-synced-device header strong,
+ .v2-pet-presence-card > header strong,
+ .v2-pet-card > header > strong
+) {
+ font-size: var(--ui-font-card-title) !important;
+ line-height: 1.35;
+}
+
/* Top-level workspace surfaces share one frame. */
.console-v2.v2-passive-display.v2-interactive-sync :is(
.v2-operation-shell,
@@ -174,6 +215,7 @@
/* Repeated inline actions use one compact size. */
.console-v2 :is(
.v2-device-adjust-toggle,
+ .v2-pet-settings-launch,
.v2-extension-nfc-launch,
.v2-extension-gateway > button,
.v2-nfc-inline button
@@ -184,6 +226,36 @@
border-radius: var(--ui-radius-control-inner);
}
+/* Related extension actions use equal columns and a shared trailing action
+ width, so every card keeps the same horizontal rhythm. */
+.console-v2 .v2-extension-sync-actions {
+ width: calc(var(--ui-inline-action-width) * 2 + 6px);
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 6px;
+}
+
+.console-v2 .v2-extension-sync-actions > :is(
+ .v2-pet-settings-launch,
+ .v2-extension-nfc-launch
+) {
+ width: 100%;
+ min-width: 0;
+ height: var(--ui-control-height-compact);
+ justify-content: center;
+ padding-inline: 7px;
+ font-size: 10px;
+}
+
+.console-v2 :is(
+ .v2-nfc-inline > button,
+ .v2-extension-gateway > button
+) {
+ width: var(--ui-inline-action-width);
+ min-width: var(--ui-inline-action-width);
+ justify-content: center;
+}
+
/* Header icon actions are distinct from circular record/send actions. */
.assistant-drawer-actions .context-reset,
.assistant-drawer-close,
@@ -246,6 +318,7 @@
.segmented > button,
.twin-selector > button,
.v2-device-adjust-toggle,
+ .v2-pet-settings-launch,
.v2-extension-nfc-launch,
.v2-extension-gateway > button,
.v2-nfc-inline button
@@ -263,6 +336,10 @@
--ui-control-height-compact: 32px;
--ui-control-size-icon: 38px;
}
+
+ .console-v2 .v2-extension-sync-actions {
+ width: 100%;
+ }
}
@media (prefers-reduced-motion: reduce) {
diff --git a/tests/e2e/test_seat_web_api.py b/tests/e2e/test_seat_web_api.py
index d6285cd..182f892 100755
--- a/tests/e2e/test_seat_web_api.py
+++ b/tests/e2e/test_seat_web_api.py
@@ -807,7 +807,7 @@ operations:
def test_static_console_and_runtime_endpoint(self) -> None:
with self.opener.open(f"{self.base_url}/", timeout=3) as response:
html = response.read().decode("utf-8")
- with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-139", timeout=3) as response:
+ with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-140", timeout=3) as response:
app_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/gesture-control.js?v=android-pad-9", timeout=3
@@ -834,15 +834,15 @@ operations:
) as response:
speech_worklet = response.read().decode("utf-8")
with self.opener.open(
- f"{self.base_url}/self-extension.js?v=pet-settings-49", timeout=3
+ f"{self.base_url}/self-extension.js?v=pet-settings-52", timeout=3
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
- f"{self.base_url}/self-extension.css?v=pet-settings-49", timeout=3
+ f"{self.base_url}/self-extension.css?v=pet-settings-52", timeout=3
) as response:
extension_styles = response.read().decode("utf-8").replace("\r\n", "\n")
with self.opener.open(
- f"{self.base_url}/assistant-chat.js?v=codex-assistant-32", timeout=3
+ f"{self.base_url}/assistant-chat.js?v=codex-assistant-33", timeout=3
) as response:
assistant_script = response.read().decode("utf-8")
with self.opener.open(
@@ -887,7 +887,7 @@ operations:
self.assertIn("CMD 0x431 · FB 0x41A · CH1", html)
self.assertIn("CMD 0x431 · FB 0x388 · CH1", html)
self.assertNotIn("透明车体 · 实体设备", html)
- self.assertIn('src="/app.js?v=dual-seat-139"', html)
+ self.assertIn('src="/app.js?v=dual-seat-140"', html)
self.assertIn('href="/styles.css?v=dual-seat-23"', html)
self.assertIn('src="/gesture-control.js?v=android-pad-9"', html)
self.assertNotIn('class="vehicle-system-hud"', html)
@@ -900,16 +900,16 @@ operations:
self.assertIn('', html)
self.assertIn('id="v2BootScreen"', html)
self.assertIn('正在加载车机控制与实时状态', html)
- self.assertIn('src="/self-extension.js?v=pet-settings-49"', html)
- self.assertIn('href="/self-extension.css?v=pet-settings-49"', html)
+ self.assertIn('src="/self-extension.js?v=pet-settings-52"', html)
+ self.assertIn('href="/self-extension.css?v=pet-settings-52"', html)
self.assertNotIn("self-calibration", html)
self.assertNotIn("self-repair", html)
self.assertNotIn("self-evolution", html)
self.assertNotIn("滚动查看更多", html)
self.assertIn('aria-label="发送消息"', html)
self.assertIn('id="assistantTranscript"', html)
- self.assertIn('href="/assistant-chat.css?v=codex-assistant-28"', html)
- self.assertIn('src="/assistant-chat.js?v=codex-assistant-32"', html)
+ self.assertIn('href="/assistant-chat.css?v=codex-assistant-29"', html)
+ self.assertIn('src="/assistant-chat.js?v=codex-assistant-33"', html)
self.assertIn('href="/energy-monitor.css?v=energy-monitor-4"', html)
self.assertIn('src="/energy-monitor.js?v=energy-monitor-4"', html)
self.assertIn('href="/codepen-glass-theme.css?v=creative-cloud-2"', html)
@@ -929,7 +929,11 @@ operations:
self.assertIn('setSidePanelVisibility("devices", false)', console_script)
self.assertIn('setSidePanelVisibility("extensions", false)', console_script)
self.assertIn('setDrawerOpen(true)', assistant_script)
- self.assertIn('($("#v2PanelControlDock") || document.body).append(trigger)', assistant_script)
+ self.assertIn('const panelDock = $("#v2PanelControlDock")', assistant_script)
+ self.assertIn(
+ "panelDock.insertBefore(trigger, firstRightPanelToggle)",
+ assistant_script,
+ )
self.assertIn('trigger.id = "energyMonitorToggle"', energy_script)
self.assertIn('document.body.append(layer)', energy_script)
self.assertIn('.energy-monitor-layer', energy_styles)
@@ -1194,14 +1198,14 @@ operations:
self.assertIn("CAN 指令已提交,等待车辆状态确认", console_script)
self.assertIn('canvas.addEventListener("webglcontextlost"', app_script)
self.assertIn('"cognitive-agent:module-change"', app_script)
- self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v302"', service_worker)
+ self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v305"', service_worker)
self.assertIn('/console-v2.css?v=delivery-state-69', service_worker)
self.assertIn('/console-v2.js?v=local-first-70', service_worker)
- self.assertIn('/assistant-chat.css?v=codex-assistant-28', service_worker)
- self.assertIn('/assistant-chat.js?v=codex-assistant-32', service_worker)
+ self.assertIn('/assistant-chat.css?v=codex-assistant-29', service_worker)
+ self.assertIn('/assistant-chat.js?v=codex-assistant-33', service_worker)
self.assertIn('/energy-monitor.css?v=energy-monitor-4', service_worker)
self.assertIn('/energy-monitor.js?v=energy-monitor-4', service_worker)
- self.assertIn('"/app.js?v=dual-seat-139"', service_worker)
+ self.assertIn('"/app.js?v=dual-seat-140"', service_worker)
self.assertIn('"/speech-input.js?v=sensevoice-13"', service_worker)
self.assertIn('"/speech-input.css?v=sensevoice-7"', service_worker)
self.assertIn('"/speech-audio-worklet.js?v=sensevoice-5"', service_worker)
@@ -1658,7 +1662,7 @@ operations:
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
- f"{self.base_url}/app.js?v=dual-seat-139", timeout=3
+ f"{self.base_url}/app.js?v=dual-seat-140", timeout=3
) as response:
app_script = response.read().decode("utf-8")
diff --git a/tests/unit/test_assistant_frontend_contract.py b/tests/unit/test_assistant_frontend_contract.py
index 1d69d63..d7204fc 100644
--- a/tests/unit/test_assistant_frontend_contract.py
+++ b/tests/unit/test_assistant_frontend_contract.py
@@ -13,15 +13,15 @@ def test_console_contains_ai_assistant_surface():
assert 'aria-label="AI助手对话"' in html
assert 'placeholder="向 AI助手提问或交代工作"' in html
assert 'aria-label="发送消息"' in html
- assert 'href="/assistant-chat.css?v=codex-assistant-28"' in html
- assert 'src="/assistant-chat.js?v=codex-assistant-32"' in html
+ assert 'href="/assistant-chat.css?v=codex-assistant-29"' in html
+ assert 'src="/assistant-chat.js?v=codex-assistant-33"' in html
def test_codex_assistant_assets_are_versioned_together_in_the_service_worker():
service_worker = (ROOT / "apps/web-console/service-worker.js").read_text(encoding="utf-8")
- assert 'const CACHE_VERSION = "cognitive-agent-pad-v302"' in service_worker
- assert '"/assistant-chat.css?v=codex-assistant-28"' in service_worker
- assert '"/assistant-chat.js?v=codex-assistant-32"' in service_worker
+ assert 'const CACHE_VERSION = "cognitive-agent-pad-v305"' in service_worker
+ assert '"/assistant-chat.css?v=codex-assistant-29"' in service_worker
+ assert '"/assistant-chat.js?v=codex-assistant-33"' in service_worker
def test_codex_frontend_uses_one_streaming_endpoint_and_agent_work_events():
@@ -121,7 +121,11 @@ def test_starting_a_new_codex_conversation_rotates_the_conversation_id():
script = (ROOT / "apps/web-console/assistant-chat.js").read_text(encoding="utf-8")
assert 'const CONVERSATION_STORAGE_KEY = "cognitive-agent.conversation-id"' in script
assert "function createConversationId()" in script
- assert "sessionStorage.setItem(CONVERSATION_STORAGE_KEY, createConversationId())" in script
+ assert "const nextConversationId = createConversationId()" in script
+ assert (
+ "sessionStorage.setItem(CONVERSATION_STORAGE_KEY, nextConversationId)"
+ in script
+ )
def test_assistant_uses_the_shared_top_right_dock_and_opens_by_default():
@@ -137,7 +141,9 @@ def test_assistant_uses_the_shared_top_right_dock_and_opens_by_default():
assert 'trigger.setAttribute("aria-pressed", String(open))' in script
assert 'drawer.toggleAttribute("inert", !open)' in script
assert 'trigger.className = "assistant-drawer-toggle v2-panel-control-button"' in script
- assert '($("#v2PanelControlDock") || document.body).append(trigger)' in script
+ assert 'const panelDock = $("#v2PanelControlDock")' in script
+ assert "panelDock.insertBefore(trigger, firstRightPanelToggle)" in script
+ assert "document.body.append(trigger)" in script
assert 'id = "assistantDrawerClose"' not in script
assert "setDrawerOpen(true)" in script
assert 'event.key === "Escape"' in script
@@ -252,8 +258,8 @@ def test_latest_same_name_mode_draft_retires_stale_card_and_lifecycle_clears_con
"async function tryModeProfileDraft", 1
)[0]
new_conversation_flow = assistant.split(
- 'if (!target?.closest("#newConversation")) return;', 1
- )[1].split("}, true);", 1)[0]
+ "function resetConversation()", 1
+ )[1].split("function initialize()", 1)[0]
assert "card.dataset.assistantModeName !== nextName" in replace_flow
assert "card.dataset.assistantModeDraft === nextRequestId" in replace_flow
@@ -266,6 +272,32 @@ def test_latest_same_name_mode_draft_retires_stale_card_and_lifecycle_clears_con
assert ".assistant-mode-draft-status.superseded" in css
+def test_new_conversation_resets_chat_in_place_and_keeps_panel_order_spatial():
+ app = (ROOT / "apps/web-console/app.js").read_text(encoding="utf-8")
+ assistant = (ROOT / "apps/web-console/assistant-chat.js").read_text(
+ encoding="utf-8"
+ )
+ reset_flow = assistant.split("function resetConversation()", 1)[1].split(
+ "function initialize()", 1
+ )[0]
+ app_flow = app.split("function startNewConversation", 1)[1].split(
+ "window.CognitiveAgentConsole", 1
+ )[0]
+
+ assert 'activeController.abort("new_conversation")' in reset_flow
+ assert "sessionStorage.removeItem(STORAGE_KEY)" in reset_flow
+ assert "renderInitialMessages()" in reset_flow
+ assert "setComposerPending(false)" in reset_flow
+ assert '"cognitive-agent:new-conversation"' in reset_flow
+ assert "window.location.reload()" not in app_flow
+ assert (
+ 'window.addEventListener("cognitive-agent:new-conversation", '
+ "startNewConversation)"
+ ) in app
+ assert 'const firstRightPanelToggle = $("[data-panel-toggle]", panelDock)' in assistant
+ assert "panelDock.insertBefore(trigger, firstRightPanelToggle)" in assistant
+
+
def test_assistant_composer_grows_to_a_cap_then_scrolls_its_own_content():
assistant = (ROOT / "apps/web-console/assistant-chat.js").read_text(encoding="utf-8")
css = (ROOT / "apps/web-console/assistant-chat.css").read_text(encoding="utf-8")
@@ -383,6 +415,18 @@ def test_chat_styles_include_accessible_responsive_and_reduced_motion_states():
assert "@media (prefers-reduced-motion: reduce)" in css
+def test_dispatched_control_card_spans_the_full_assistant_message_row():
+ css = (ROOT / "apps/web-console/assistant-chat.css").read_text(encoding="utf-8")
+ layout = css.split("/* Compact command receipt", 1)[1].split(
+ "/* The function-mode strip", 1
+ )[0]
+
+ assert "grid-template-columns: 30px minmax(0, 1fr)" in layout
+ assert "display: contents" in layout
+ assert "grid-column: 1 / -1" in layout
+ assert "max-width: none" in layout
+
+
def test_assistant_does_not_restore_collapsed_sidebars_and_uses_mobile_overlays():
script = (ROOT / "apps/web-console/assistant-chat.js").read_text(encoding="utf-8")
css = (ROOT / "apps/web-console/assistant-chat.css").read_text(encoding="utf-8")
diff --git a/tests/unit/test_energy_monitor_frontend_contract.py b/tests/unit/test_energy_monitor_frontend_contract.py
index 4ac2aeb..02b80f5 100644
--- a/tests/unit/test_energy_monitor_frontend_contract.py
+++ b/tests/unit/test_energy_monitor_frontend_contract.py
@@ -35,10 +35,10 @@ def test_energy_monitor_assets_are_versioned_and_cached() -> None:
):
assert asset in html
assert f'"{asset}"' in service_worker
- assert html.index("/assistant-chat.js?v=codex-assistant-32") < html.index(
+ assert html.index("/assistant-chat.js?v=codex-assistant-33") < html.index(
"/energy-monitor.js?v=energy-monitor-4"
)
- assert 'const CACHE_VERSION = "cognitive-agent-pad-v302"' in service_worker
+ assert 'const CACHE_VERSION = "cognitive-agent-pad-v305"' in service_worker
def test_energy_monitor_uses_the_fifth_shared_dock_icon_and_starts_closed() -> None:
diff --git a/tests/unit/test_frontend_theme_contract.py b/tests/unit/test_frontend_theme_contract.py
index d06a96f..672d883 100644
--- a/tests/unit/test_frontend_theme_contract.py
+++ b/tests/unit/test_frontend_theme_contract.py
@@ -42,13 +42,13 @@ def test_theme_asset_versions_are_consistent_with_the_pwa_shell() -> None:
for asset in (
"/styles.css?v=dual-seat-23",
"/console-v2.css?v=delivery-state-69",
- "/self-extension.css?v=pet-settings-49",
+ "/self-extension.css?v=pet-settings-52",
"/speech-input.css?v=sensevoice-7",
"/codepen-glass-theme.css?v=creative-cloud-2",
):
assert asset in html
assert f'"{asset}"' in service_worker
- assert 'const CACHE_VERSION = "cognitive-agent-pad-v302"' in service_worker
+ assert 'const CACHE_VERSION = "cognitive-agent-pad-v305"' in service_worker
def test_reference_glass_theme_covers_the_complete_workspace() -> None:
diff --git a/tests/unit/test_nfc_frontend_contract.py b/tests/unit/test_nfc_frontend_contract.py
index 4389147..a50f9a2 100644
--- a/tests/unit/test_nfc_frontend_contract.py
+++ b/tests/unit/test_nfc_frontend_contract.py
@@ -60,8 +60,8 @@ def test_unified_console_exposes_device_id_based_nfc_authorization() -> None:
assert '.v2-nfc-command-list' in styles
assert '.v2-nfc-command-option' in styles
assert '.v2-nfc-select-all' in styles
- assert 'self-extension.js?v=pet-settings-49' in html
- assert 'self-extension.css?v=pet-settings-49' in html
+ assert 'self-extension.js?v=pet-settings-52' in html
+ assert 'self-extension.css?v=pet-settings-52' in html
def test_unified_console_exposes_gscu_tcp_configuration_and_actions() -> None:
diff --git a/tests/unit/test_requested_frontend_enhancements.py b/tests/unit/test_requested_frontend_enhancements.py
index 67b1f43..e060f9d 100644
--- a/tests/unit/test_requested_frontend_enhancements.py
+++ b/tests/unit/test_requested_frontend_enhancements.py
@@ -1110,7 +1110,8 @@ def test_interactive_console_uses_one_top_right_icon_dock_for_all_five_panels()
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 'const panelDock = $("#v2PanelControlDock")' in assistant
+ assert "panelDock.insertBefore(trigger, firstRightPanelToggle)" 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
diff --git a/tests/unit/test_spatial_ui_consistency_contract.py b/tests/unit/test_spatial_ui_consistency_contract.py
index 921ac08..6185c82 100644
--- a/tests/unit/test_spatial_ui_consistency_contract.py
+++ b/tests/unit/test_spatial_ui_consistency_contract.py
@@ -12,7 +12,7 @@ def source(name: str) -> str:
def test_spatial_ui_contract_is_loaded_last_and_cached() -> None:
html = source("index.html")
service_worker = source("service-worker.js")
- asset = "/ui-consistency.css?v=spatial-ui-4"
+ asset = "/ui-consistency.css?v=spatial-ui-5"
assert asset in html
assert f'"{asset}"' in service_worker
@@ -25,6 +25,10 @@ def test_same_kind_surfaces_share_geometry_tokens() -> None:
for token in (
"--ui-control-height-compact:",
"--ui-control-height:",
+ "--ui-font-card-title:",
+ "--ui-font-section-title:",
+ "--ui-font-panel-title:",
+ "--ui-inline-action-width:",
"--ui-radius-control:",
"--ui-radius-item:",
"--ui-radius-section:",
@@ -59,6 +63,7 @@ def test_same_kind_controls_share_sizes_and_focus_treatment() -> None:
"#v2PanelControlDock > .v2-panel-control-button",
".v2-mode-rail .mode-rail-actions > button",
".v2-device-adjust-toggle",
+ ".v2-pet-settings-launch",
".v2-extension-nfc-launch",
".energy-trend-tabs button",
".mode-gallery-close",
@@ -71,6 +76,44 @@ def test_same_kind_controls_share_sizes_and_focus_treatment() -> None:
assert "outline: none" in styles
+def test_panel_titles_and_extension_actions_share_type_and_size_tokens() -> None:
+ styles = source("ui-consistency.css")
+
+ for selector in (
+ ".v2-operation-heading h2",
+ ".joint-twin-shell .twin-heading h3",
+ ".v2-device-sync-board > header h3",
+ ".v2-extension-sync-heading h3",
+ ".v2-device-card-focus strong",
+ ".v2-nfc-inline strong",
+ ".v2-extension-gateway strong",
+ ):
+ assert selector in styles
+
+ assert "font-size: var(--ui-font-panel-title) !important" in styles
+ assert "font-size: var(--ui-font-card-title) !important" in styles
+ assert "grid-template-columns: repeat(2, minmax(0, 1fr))" in styles
+ assert "width: var(--ui-inline-action-width)" in styles
+
+
+def test_pet_and_tcp_dialog_controls_keep_accessible_aligned_layout() -> None:
+ styles = source("self-extension.css")
+ extension = source("self-extension.js")
+ tcp_form = styles.split(".v2-gscu-config-form {", 1)[1].split("}", 1)[0]
+ tcp_submit = styles.split(
+ ".v2-gscu-config-form > .v2-extension-primary {", 1
+ )[1].split("}", 1)[0]
+
+ assert ".v2-extension-dialog .v2-pet-card-region > header > button" in styles
+ assert "min-width: 104px" in styles
+ assert "align-items: end" in tcp_form
+ assert "height: 40px" in tcp_submit
+ assert "margin: 0" in tcp_submit
+ assert 'id="extensionGscuTcpHostHint"' in extension
+ assert 'id="extensionGscuTcpPortHint"' in extension
+ assert 'class="visually-hidden" id="extensionPetSettingsDescription"' in extension
+
+
def test_compact_sidebar_overrides_cannot_break_shared_geometry() -> None:
styles = source("ui-consistency.css")
diff --git a/tests/unit/test_vue_frontend_contract.py b/tests/unit/test_vue_frontend_contract.py
index 714772e..bedce05 100644
--- a/tests/unit/test_vue_frontend_contract.py
+++ b/tests/unit/test_vue_frontend_contract.py
@@ -18,14 +18,14 @@ def test_vue_presentation_assets_are_local_ordered_and_cached() -> None:
presentation = "/vue-ui.js?v=vue-presentation-3"
assert html.index(runtime) < html.index(presentation) < html.index(
- "/app.js?v=dual-seat-139"
+ "/app.js?v=dual-seat-140"
)
assert html.index(presentation) < html.index(
- "/assistant-chat.js?v=codex-assistant-32"
+ "/assistant-chat.js?v=codex-assistant-33"
)
assert f'"{runtime}"' in service_worker
assert f'"{presentation}"' in service_worker
- assert 'const CACHE_VERSION = "cognitive-agent-pad-v302"' in service_worker
+ assert 'const CACHE_VERSION = "cognitive-agent-pad-v305"' in service_worker
assert "unpkg.com" not in html
assert "cdn.jsdelivr.net" not in html