From f93994ad69643334681b21a4d7fd586016e0cd4c Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 10 Sep 2026 13:17:50 +0200 Subject: [PATCH] fix: collapsed strip, workspace-mode search, image dialog chat list - Collapsed menu: its own plain white 52px column against the tiled ground, no border line, in both views. Flat, it had no edge. - Collapsed + workspace launcher: assistant.css set the rail track to 0 and beat the 52px rule, so the collapsed menu (name card included) vanished. - Search follows the view, not the URL: the workspace launcher keeps /assistant, so Workspace searched chats. - Create image, "Base it on": a native opens a list as wide as its longest option, so full chat + // titles pushed "Base it on" far past the dialog. Each label keeps as many + // whole words as fit the select's own width; the full title stays on hover. + var OPTION_CHROME = 44; // list padding + scrollbar, which the text cannot use + function fitSelectOptions(select) { + if (!select) return; + var style = window.getComputedStyle(select); + var room = select.clientWidth - OPTION_CHROME; + if (room <= 0) return; // not laid out (or no layout engine): leave labels alone + var ctx = fitSelectOptions.ctx || (fitSelectOptions.ctx = document.createElement('canvas').getContext('2d')); + if (!ctx) return; + ctx.font = style.fontWeight + ' ' + style.fontSize + ' ' + style.fontFamily; + Array.prototype.forEach.call(select.options, function (opt) { + var full = opt.getAttribute('data-full-title') || opt.textContent; + opt.setAttribute('data-full-title', full); + opt.title = full; + if (ctx.measureText(full).width <= room) { opt.textContent = full; return; } + var words = full.split(/\s+/), text = ''; + for (var i = 0; i < words.length; i++) { + var next = text ? text + ' ' + words[i] : words[i]; + if (ctx.measureText(next + '…').width > room) break; + text = next; + } + // One word wider than the box: fall back to cutting characters. + if (!text) { text = full; while (text.length > 1 && ctx.measureText(text + '…').width > room) text = text.slice(0, -1); } + opt.textContent = text.replace(/[\s.,;:!?-]+$/, '') + '…'; + }); + } + window.addEventListener('resize', function () { fitSelectOptions(document.getElementById('create-image-chat')); }); + // Cuts at a word boundary, and only adds an ellipsis when something was // actually dropped. function truncateOnWord(text, limit) { diff --git a/test/assistant-workspace-layout.test.js b/test/assistant-workspace-layout.test.js index 517a87ee..6e791fa1 100644 --- a/test/assistant-workspace-layout.test.js +++ b/test/assistant-workspace-layout.test.js @@ -568,3 +568,45 @@ test('desktop: the assistant rail is the app sidebar, flush and flat', () => { assert.match(rail, /\.assistant-history \.account-card \{ padding:8px; \}/); assert.match(styles, /body\.assistant-workspace\.menu-hidden \.assistant-history \{[^}]*padding:0; \}/, 'collapsed adds no gap of its own'); }); + +test('the collapsed menu is a bar of its own, in both views', () => { + const css = read('public/css/styles.css'); + // Flat on the tiled ground, the 52px strip had no edge: its icons looked + // pressed against the page card. Expanded stays flat; collapsed gets a bar. + const desk = css.slice(css.indexOf('@media (min-width:769px) {\n /* Collapsing is a desktop idea')); + const block = desk.slice(0, desk.indexOf('\n}\n')); + // Its own white column, no border line: the white against the tiles is the edge. + assert.match(block, /body\.menu-hidden \.sidebar,\s*body\.assistant-workspace\.menu-hidden \.assistant-history \{ background:white; \}/); + assert.match(css, /@media\(min-width:769px\)\{ \.sidebar\{ background:transparent; border-right:none; \} \}/, 'expanded stays flat'); +}); + +test('search follows the view: Workspace searches the workspace even inside the assistant', () => { + const app = read('public/js/app.js'); + const fn = app.slice(app.indexOf('function searchSources()'), app.indexOf("return { kind: 'tabs'")); + // Keyed on the URL, the workspace launcher (still at /assistant) searched chats. + assert.doesNotMatch(fn, /location\.pathname === '\/assistant'/); + assert.match(fn, /classList\.contains\('assistant-workspace'\)\s*&& !document\.body\.classList\.contains\('assistant-mode-workspace'\)/); + assert.match(fn, /if \(inChats && typeof window\.assistantSearchableChats === 'function'\)/); +}); + +test('collapsed workspace mode keeps the 52px strip instead of losing the menu', () => { + const css = read('public/css/assistant.css'); + // This track said 0 and, loading after styles.css, won — the collapsed menu + // and its name card disappeared whenever the workspace launcher showed. + assert.doesNotMatch(css, /body\.assistant-mode-workspace\.menu-hidden \.assistant-layout \{ grid-template-columns:0 /); + assert.match(css, /body\.assistant-mode-workspace\.menu-hidden \.assistant-layout \{ grid-template-columns:52px minmax\(0,1fr\); \}/); +}); + +test('the create-image chat list never grows wider than its dialog', () => { + const js = read('public/js/clinicalAssistant.js'); + // A native