diff --git a/docs/APPROVED_UI_OVERHAUL.md b/docs/APPROVED_UI_OVERHAUL.md new file mode 100644 index 0000000..fce2876 --- /dev/null +++ b/docs/APPROVED_UI_OVERHAUL.md @@ -0,0 +1,29 @@ +# Approved Hope web interface overhaul + +The user approved the standalone HTML proposal, including personal settings and administration, with “do it.” This document records the implementation boundary and evidence. + +## Implemented +- Square 48px Lightwell realtime action inside the composer; direct intentional connection initiation, not dictation and not a portal to another Start button. +- Inline voice console with actual connection, agent and microphone state, mute/end, selected-conversation ownership, and retained editable typed draft. Text send stays disabled while voice owns the conversation. +- Compact header and conversation-action overflow; workspace editing behind progressive controls; sidebar Library, Activity, and Account & settings. +- Library mounts existing files/artifact controls. Activity mounts actual scheduled tasks. Memory controls are mounted in personal settings without duplicating owners or handlers. +- Real personal settings: readonly account identity, saved appearance, actual voice catalog, server-managed model/thinking, memory, security guidance, current-device sign-out. +- Real administrator account list, creation, password reset, deletion, and retry for failed provisioning. No invented profile writes, existing-role editor, device-session list, connector management or temporary-chat privacy. +- Existing cookie auth, role enforcement and backend contracts preserved. Account change closes dialogs and clears private UI. Appearance loads/saves are account-epoch guarded. +- Shared semantic theme surfaces fix the legacy light-theme voice transcript contrast defect; reduced-motion matrix avoids live layout size mutations. + +## Qualification before deployment +- Integrated browser/theme/package/token/rollback selection: 45 passed, 304 subtests. +- Final conversation/settings recheck after appearance-save epoch guard: 12 passed. +- Responsive captures verified 1440/390/320 widths: no horizontal overflow, composer action exactly 48×48. +- Mocked SDK lifecycle covers direct start, real-readiness gating, mute/unmute, cid binding, stop and microphone rejection. This is not acoustic evidence. +- Account/backend isolation regression selection: 25 passed. + +## Deployment +Clean committed archive e59c712, separate NAS staging path (no overwrite of dirty NAS checkout), voice image sha256:02bd8e47c2a802ac2232f1403de570e7fb843472b03037167e0ea1e124a048dc. Existing control API image retained. Deployment script produced healthy services with protected quiesced account-state backup and retained rollback containers suffix 20261003T172419Z. External Hope Music is outside this release. + +## Live verification +Live qualification scripts are explicit, credential-protected and use disposable conversations. Personal settings/admin verification is read-only: no account or memory changes. Text verification exercises stream completion, overflow Pin, reload and cleanup. Synthetic microphone verification exercises Azure STT → Hermes → Azure TTS → browser audio, not physical acoustic quality. Collected live release verification passed: all personal settings, actual admin list and mobile settings navigation; text response/overflow Pin/reload; direct composer-start synthetic speech with Azure user transcription, expected Hermes answer, playing LiveKit TTS track, 32,067 received audio bytes, zero browser errors, and successful disposable voice-conversation cleanup. No live accounts or memory were mutated by settings verification. + +## Remaining boundaries +This release changes the web interface; it does not install a native redesign. Physical microphone/speaker quality and device-specific mobile keyboard behavior remain separate human/device qualification. Native prior signed app installation is not evidence of this web overhaul. Unsupported capabilities remain unavailable rather than simulated in production. diff --git a/scripts/qualify-chat-browser.py b/scripts/qualify-chat-browser.py index c008050..4a93ddf 100644 --- a/scripts/qualify-chat-browser.py +++ b/scripts/qualify-chat-browser.py @@ -19,6 +19,7 @@ with sync_playwright() as p: expect(page.locator('#cancelRun')).to_be_hidden(timeout=180000) cid=page.evaluate('HopeChat.conversationId') expect(page.locator('#chatTimeline')).to_contain_text('Hope') + page.locator('#conversationMenu').click() page.locator('#pinConversation').click() expect(page.locator('#pinConversation')).to_contain_text('Unpin') page.reload(wait_until='networkidle') diff --git a/scripts/qualify-settings-live.py b/scripts/qualify-settings-live.py new file mode 100644 index 0000000..f794793 --- /dev/null +++ b/scripts/qualify-settings-live.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +"""Read-only deployed settings/admin browser qualification; no account mutations.""" +import json +from pathlib import Path +from playwright.sync_api import sync_playwright, expect +values=json.loads(Path('.runtime/deployment.json').read_text()) +with sync_playwright() as p: + browser=p.chromium.launch(headless=True) + context=browser.new_context(ignore_https_errors=True,viewport={'width':1440,'height':1000}) + page=context.new_page();errors=[] + page.on('pageerror',lambda e:errors.append(str(e))) + page.goto('https://192.168.86.2:9178/',wait_until='networkidle') + page.locator('#loginUsername').fill('admin');page.locator('#loginPassword').fill(values['admin_password']);page.locator('#loginSubmit').click() + expect(page.locator('#primaryInput')).to_be_enabled(timeout=30000) + page.locator('#accountMenuBtn').click();expect(page.locator('#settingsDialog')).to_be_visible() + expect(page.locator('#settingsAccountRole')).to_contain_text('Administrator') + page.locator('[data-settings-target=appearance]').click();expect(page.locator('#paletteSelect')).to_be_enabled(timeout=15000) + page.locator('[data-settings-target=preferences]').click();expect(page.locator('#modelSelect')).to_be_enabled(timeout=30000) + expect(page.locator('#voiceList [role=radio]').first).to_be_visible(timeout=30000) + page.locator('[data-settings-target=privacy]').click();expect(page.locator('#settingsMemoryStatus')).to_contain_text('saved memory',timeout=30000) + page.locator('[data-settings-target=security]').click();expect(page.locator('[data-settings-panel=security]')).to_be_visible() + page.locator('#adminBtn').click();expect(page.locator('#usersBody tr').first).to_be_visible(timeout=15000) + page.screenshot(path='.runtime/hope-approved-admin-live.png',full_page=True) + assert page.evaluate('document.documentElement.scrollWidth<=innerWidth') + page.locator('#settingsClose').click();page.set_viewport_size({'width':390,'height':844}) + page.locator('#drawerOpen').click();page.locator('#accountMenuBtn').click();expect(page.locator('#settingsDialog')).to_be_visible() + assert page.evaluate('document.documentElement.scrollWidth<=innerWidth') + page.screenshot(path='.runtime/hope-approved-settings-mobile-live.png',full_page=True) + page.locator('#settingsClose').click();assert not errors,errors + page.evaluate("HopeAuth.request('/api/auth/logout',{method:'POST'})") + context.close();browser.close() + print('live_settings_account_appearance_voice_model_memory_security_admin_mobile=PASS; console_errors=0; no_mutations') diff --git a/web/style.css b/web/style.css index e885ecd..7ca70d1 100644 --- a/web/style.css +++ b/web/style.css @@ -807,7 +807,14 @@ dialog p{ overflow-wrap:anywhere} .admin-section th{ color:var(--ink-soft); - font-weight:500} + font-weight:500; + white-space:nowrap} +.admin-section td:nth-child(2),.admin-section td:nth-child(3){ + white-space:nowrap; + overflow-wrap:normal} +.admin-section td:nth-child(4){ + overflow-wrap:break-word; + min-width:140px} .user-actions .btn{ font-size:11px; padding:8px;