hermes: declutter chat to a chat-only surface
Extend the chat router HTML-injection bridge so tenants see a clean chat + conversation-history experience instead of the full agent cockpit. hideChatAdministration now hides the rail buttons and panels for logs, insights, memory, skills, workspaces, todos, tasks and profiles (alongside the existing Kanban), and bridgeCSS hides the same [data-panel] targets pre-JS to avoid a flash. Also hide the redundant legacy settings model dropdown (#settingsModel) via bridgeCSS while keeping the composer routing chip. Chat, history/sessions, new-chat, composer and the model chip are untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2b2c8bda14
commit
f83c59a1c9
@ -367,11 +367,52 @@ func TestPrivateFileBrowserIsTenantAuthenticatedAndHidesKanban(t *testing.T) {
|
||||
assetRequest.Header.Set("X-Forwarded-User", "subject")
|
||||
assetResponse := httptest.NewRecorder()
|
||||
router.ServeHTTP(assetResponse, assetRequest)
|
||||
if !strings.Contains(assetResponse.Body.String(), "endpoint('list'") || !strings.Contains(assetResponse.Body.String(), `'[data-panel="kanban"]'`) {
|
||||
if !strings.Contains(assetResponse.Body.String(), "endpoint('list'") || !strings.Contains(assetResponse.Body.String(), `'[data-panel="' + panel + '"]'`) {
|
||||
t.Fatal("file browsing or chat-only navigation policy is missing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatBridgeHidesOnlyNonChatCockpitChrome(t *testing.T) {
|
||||
router, err := newTenantRouter(filepath.Join(t.TempDir(), "state.json"), 1, func(slot int) string { return "" })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
asset := func(path string) string {
|
||||
request := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
request.Header.Set("X-Forwarded-User", "subject")
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("%s got status %d", path, response.Code)
|
||||
}
|
||||
return response.Body.String()
|
||||
}
|
||||
js := asset("/hermes-chat-bridge.js")
|
||||
css := asset("/hermes-chat-bridge.css")
|
||||
|
||||
// The whole cockpit (Kanban plus every non-chat rail panel) is declutter-hidden
|
||||
// both pre-JS (CSS, no flash) and by the runtime .hidden reinforcement.
|
||||
for _, panel := range []string{"kanban", "logs", "insights", "memory", "skills", "workspaces", "todos", "tasks", "profiles"} {
|
||||
if !strings.Contains(css, `[data-panel="`+panel+`"]`) {
|
||||
t.Fatalf("bridge CSS does not hide cockpit panel %q", panel)
|
||||
}
|
||||
if !strings.Contains(js, `'`+panel+`'`) {
|
||||
t.Fatalf("bridge JS does not mark cockpit panel %q hidden", panel)
|
||||
}
|
||||
}
|
||||
if !strings.Contains(css, "display:none") || !strings.Contains(css, "#settingsModel") {
|
||||
t.Fatal("bridge CSS must hide the cockpit panels and the redundant settings model selector")
|
||||
}
|
||||
|
||||
// The clean chat experience (chat surface, history, composer, model chip)
|
||||
// must never be part of the hide set.
|
||||
for _, keep := range []string{`[data-panel="chat"]`, `[data-panel="history"]`, `[data-panel="sessions"]`, `[data-panel="composer"]`, "composerRoutingChip", "composerRoutingLabel"} {
|
||||
if strings.Contains(css, keep) || strings.Contains(js, keep) {
|
||||
t.Fatalf("declutter wrongly targets a core chat control: %q", keep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebUIModelAndReasoningOverridesAreProxied(t *testing.T) {
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
if request.Method != http.MethodPost {
|
||||
|
||||
@ -93,6 +93,8 @@ const privateFilesPage = `<!doctype html>
|
||||
</html>`
|
||||
|
||||
const bridgeCSS = `
|
||||
[data-panel="kanban"],[data-panel="logs"],[data-panel="insights"],[data-panel="memory"],[data-panel="skills"],[data-panel="workspaces"],[data-panel="todos"],[data-panel="tasks"],[data-panel="profiles"]{display:none!important}
|
||||
#settingsModel,label:has(#settingsModel),.settings-row:has(#settingsModel){display:none!important}
|
||||
#hermes-files-sidebar,#hermes-telegram-sidebar{display:flex;align-items:center}
|
||||
.hermes-link-page{margin:0;min-height:100vh;display:grid;place-items:center;background:#0f172a;color:#e2e8f0;font:16px/1.5 system-ui,sans-serif}
|
||||
.hermes-link-card{width:min(620px,calc(100% - 40px));box-sizing:border-box;padding:32px;border:1px solid #334155;border-radius:18px;background:#111827;box-shadow:0 20px 60px #0006}
|
||||
@ -105,8 +107,11 @@ const bridgeCSS = `
|
||||
const bridgeJS = `(() => {
|
||||
const page = document.querySelector('[data-telegram-page]');
|
||||
const filesPage = document.querySelector('[data-files-page]');
|
||||
const chatAdministrationPanels = ['kanban','logs','insights','memory','skills','workspaces','todos','tasks','profiles'];
|
||||
const hideChatAdministration = () => {
|
||||
document.querySelectorAll('[data-panel="kanban"]').forEach((node) => { node.hidden = true; });
|
||||
chatAdministrationPanels.forEach((panel) => {
|
||||
document.querySelectorAll('[data-panel="' + panel + '"]').forEach((node) => { node.hidden = true; });
|
||||
});
|
||||
};
|
||||
hideChatAdministration();
|
||||
if (!page && !filesPage) {
|
||||
@ -159,7 +164,7 @@ const bridgeJS = `(() => {
|
||||
const observer = new MutationObserver(() => {
|
||||
if (scheduled) return;
|
||||
scheduled = true;
|
||||
requestAnimationFrame(() => { scheduled = false; installSidebarLinks(); });
|
||||
requestAnimationFrame(() => { scheduled = false; hideChatAdministration(); installSidebarLinks(); });
|
||||
});
|
||||
installSidebarLinks();
|
||||
observer.observe(document.body, {childList:true, subtree:true});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user